Associating customers

Call /v1/referrals/associate at signup to turn a visitor into a referred customer, then review and correct referrals.

Embed
Preview of the card
<a href="https://referalli.com/docs/associating-customers"><img src="https://referalli.com/docs/_api/public/orgs/07f432f4-34b9-46a7-aaad-c0e395d2e4f9/spaces/2bf4f614-3630-4fbf-85f9-5ecafa6b7d97/pages/ffea3e22-3fcb-49f6-b7b9-969e92819ca0/social-card.svg?v=3e7ae33eec8b" alt="Associating customers" width="600" height="315"></a>

POST /v1/referrals/associate is the most important server-side call in an integration. You make it once when a customer signs up. Referalli records the facts you send, runs the attribution engine, and, if a partner earned the customer, creates a referral that later payments are matched against.

Make the call

curl -X POST https://referalli.com/v1/referrals/associate \
  -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -d '{
    "programId": "prg_...",
    "customerRef": "cus_NffrFeUfNV2Hib",
    "anonId": "a_k3j9x0q7m2c8v1b5n4z6",
    "referralToken": "clk_01JB...",
    "emailHash": "b4c9a2...",
    "ip": "203.0.113.7",
    "ua": "Mozilla/5.0 ...",
    "idempotencyKey": "signup:usr_8812"
  }'

Field

Required

Description

programId

yes

The program the customer is joining.

customerRef

yes

Your id for the customer, 1 to 256 characters. Use the id your billing provider sends in its webhooks (for Stripe, the cus_… id). Payments are matched to referrals by this value.

anonId

no

The visitor's anonymous id from aff.getReferral().anonId. Finds the clicks tied to that browser.

referralToken

no

A click id (ref_click_id from the landing URL, or aff.getReferral().clickId). The most precise signal.

couponCode

no

A partner coupon code the customer entered.

emailHash

no

Lowercase hex SHA-256 of the lowercased email.

ip, ua

no

The customer's IP and user agent, used only for the fingerprint fallback.

idempotencyKey

no

Up to 128 characters. Repeating a key returns the existing referral without recording anything new.

emailDomain

no

The customer's email domain, such as mailinator.com. emailHash hides the domain, so send this as well to have sign-ups from burner-email providers held for review (see Attribution).

occurredAt

no

When the signup happened (ISO 8601), for a call made late, such as one queued after an outage or a backfill. The referral, its window and attribution are dated by it. A time more than 5 minutes in the future returns 400. Default: now.

Send whatever you have. More signals give the engine more to work with; none of the optional fields is required for a match.

The response

{
  "referral": {
    "id": "ref_01JB...",
    "programId": "prg_...",
    "partnerId": "ptr_...",
    "customerRef": "cus_NffrFeUfNV2Hib",
    "source": "click",
    "matchedVia": "deterministic",
    "confidence": 1,
    "clickId": "clk_01JB...",
    "linkId": "lnk_...",
    "status": "active",
    "ts": "2026-10-21T14:03:11.000Z",
    "windowExpiresAt": "2027-10-21T14:03:11.000Z",
    "touchCredits": [{ "partnerId": "ptr_...", "weight": 1, "clickId": "clk_01JB..." }],
    "reasoning": { "model": "last_click", "consideredTouchpoints": 1, "windowDays": 60, "notes": [] }
  }
}
  • referral is null when no partner matched. That is a normal outcome, not an error. When a partner was skipped (banned, flagged, or the customer is that partner), the response adds reasoning.notes saying so. See Partners who can't win.

  • matchedVia is coupon, deterministic, email_hash or probabilistic; confidence is 1, 1, 0.9 and 0.6 respectively. See Attribution.

  • source is coupon for coupon matches and click otherwise.

  • windowExpiresAt is ts plus the program's signupToConversionDays. Payments after that date earn nothing.

Calling it more than once

A customer has at most one non-void referral per program. If one already exists, associate records the new facts and returns the existing referral unchanged: first attribution wins. Calling it for every login is safe but unnecessary.

When a referral is created, Referalli emits referral.created, records a signup conversion, and pays a signup bounty if the program's rule defines one, the referral is active, and the partner is active in your org.

Referral statuses

Status

Meaning

Earns commissions

active

Attributed and in good standing.

yes

pending_review

Held for a person to check. Happens for fingerprint-only matches (unless the program's probabilisticPolicy is auto; see Attribution), and for self-referrals on any channel, coupons included: the customer's email hash equals the partner's own email (Self-referrals). The associate response already shows it.

no

paused

Switched off by you, reversibly. Its payments are still recorded as conversions, so they count for reporting and for "first payment", but they earn nothing until it's active again. It also doesn't count toward the partner's level while paused.

no

void

Cancelled, permanently.

no

Review and correct referrals

Call

Effect

Roles

GET /v1/referrals

Referrals, newest first and paginated (the latest 500 under 2026-10-21). Filters: programId, partnerId, status, customerRef.

any member

GET /v1/referrals/{id}

One referral, in the same shape associate returns. 404 if unknown.

any member

PATCH /v1/referrals/{id}

Body { "status": "paused" } or { "status": "active" }. Pauses an active referral or resumes a paused one. Other transitions return 409; other statuses return 400. Audited, and emits the referral.paused / referral.resumed webhook.

owner, admin

GET /v1/referrals/{id}/journey

The referral plus every touchpoint considered: clickId, ts, url, linkId, fraudScore, eligible, weight, winner.

any member

POST /v1/referrals/{id}/approve

pending_review to active. 404 if the referral is not pending review.

owner, admin

POST /v1/referrals/{id}/reassign

Body { "partnerId": "ptr_..." }. Moves credit to another partner and marks the match manual.

owner, admin

POST /v1/referrals/{id}/void

Sets the status to void.

owner, admin

Approving or reassigning affects payments that arrive afterwards. Commission lines already earned stay with the partner who earned them; correct those through the ledger.

Auth platforms instead of code

Whatever auth you use, Referalli can make the associate call for you: your auth server calls the signed protocol, Atlas does it natively, and Clerk, Supabase and WorkOS connect through their own user webhooks. See Auth platforms.

Updated