Skip to content

Integrating Referrals

Referral endpoints live under /projects/{projectId}/referrals. This page shows the calls an app makes. The model (codes, validate vs apply, config) is covered in referral concepts. Rewards are set up with rules in the referral program guide.

  1. Referrer opens “Invite friends”: get their code, or create it if they don’t have one yet.
  2. Referee types a code at signup: validate it for instant feedback.
  3. Referee account is created: apply the code with the referee’s userId.
  4. Referee does the qualifying action: your app sends the business event; a rule pays the rewards.
GET /projects/{projectId}/referrals/referrer/cus_alice

Returns the code, or 404 3102 if Alice has none. Then create one:

POST /projects/{projectId}/referrals

{ "referrerId": "cus_alice" }

The code string is generated from the project config. Optional fields: code (your own string, must be unique), maxUses (positive integer or null), expiresAt, tags. Creating a second code for the same referrer fails with 400 3100, so use get-then-create, not create-and-ignore.

System codes (no owner) work the same way without referrerId. For many at once:

POST /projects/{projectId}/referrals/bulk

{ "count": 200, "tags": ["partner:acme"], "maxUses": 1, "expiresAt": "2027-01-01T00:00:00Z" }

POST /projects/{projectId}/referrals/validate

{ "code": "K7M2QX9A", "refereeId": "cus_bob" }

A valid code returns { "valid": true, "code": { … } }. An invalid code returns an error response (404 3101, or 400 3103–3107); map it to a user-facing message. Leave out refereeId if the account does not exist yet; the self-referral and already-referred checks then run at apply time.

POST /projects/{projectId}/referrals/apply

{ "code": "K7M2QX9A", "refereeId": "cus_bob" }

Returns the new referral record (with referrerId) and increments the code’s currentUses. Apply is safe to retry: a second apply for the same referee fails with 3107 and changes nothing.

To apply from an event instead (for example a user_signed_up event carrying the code), use the rule action:

{
"type": "apply_referral",
"config": {
"code": "{{ $('event').payload.referralCode }}",
"refereeId": "{{ $('event').userId }}"
}
}

Add a condition {{ $('event').payload.referralCode }} isNotEmpty so signups without a code don’t produce failed actions.

NeedRequest
Code details by code stringGET /referrals/lookup/{code}
Code details by IDGET /referrals/{codeId}
Who referred Bob?GET /referrals/usages?refereeId=cus_bob
Everyone Alice referredGET /referrals/usages?referrerId=cus_alice&limit=100
Usage chart for one codeGET /referrals/{codeId}/analytics
Top referrersGET /referrals/leaderboard?limit=10
Codes by owner typeGET /referrals?type=user or ?type=system, also status, tag, referrerId

Lists return { "items": [...], "total": n } with the newest first; limit is 1–100 (default 50). start/end filter by creation date.

PATCH /projects/{projectId}/referrals/K7M2QX9A { "status": "inactive" }
DELETE /projects/{projectId}/referrals/K7M2QX9A

PATCH and DELETE take the code string, not the code ID. Deactivating is reversible ("status": "active"). Deleting removes the code (later validation returns 3101) but keeps the referral records it produced. After a delete, the referrer can be given a new code.

  • GET /referrals/usages?refereeId=… returns exactly one record with the expected referrerId.
  • GET /referrals/lookup/{code} shows currentUses increased by one.
  • A validation success is not attribution, and attribution is not a reward. Check each separately; rewards are checked in the referee’s and referrer’s wallets.