Skip to main content
Documentation
API Reference

Referrals

Referrals endpoints claim an install for a referral link, list and read credited referrals, resend their webhook events, and delete a referral.

Referrals endpoints record which invitation link brought an install, and give you the report behind the dashboard's Referrals view. Read Referrals for the full path from link to reward. The MCP server has the same operations as claim_referral, list_referrals, get_referral, resend_referral_webhook and delete_referral.

Authentication and Permissions

Reading needs an API key with the referrals:read scope. Claiming, resending and deleting need referrals:write, which includes referrals:read. Resending also needs a key whose creator is an owner or admin.

POST /v1/referrals/claims is the one referral route an SDK key can call. It needs the attribution:write scope an SDK key carries, and it is limited to 60 requests a minute per key per client IP address. Every other referral route refuses an SDK key with 403 SDK_KEY_ROUTE_FORBIDDEN.

Claim a Referral

POST/referrals/claims

Claim a referral

Credits an installer's install to the referral link that brought it. Send it from your backend with an API key that has the referrals:write scope, or from the SDK with an SDK key that has attribution:write. The first valid claim for an app and installer id is recorded as claimed. A later claim for the same installer is ignored and points at the first through duplicate_of. A claim made after the credit window closed is recorded as flagged with the outside_window flag. An exact match (a token, an install referrer, a device id or a link open) replaces an earlier estimate for the same installer: the referral keeps its id, moves to the exact link, and a referral.updated webhook event follows. An estimate is accepted only for an iPhone install when the app has handoff off and probabilistic matching on. A link_open match counts only when the app credits existing users. An install from an ordinary link, or a tap before the link became a referral link, gives none. Every outcome is a 200 with a status of claimed, flagged, ignored or none; none records nothing. Send the same Idempotency-Key to repeat a call safely.

Parameters

NameTypeInDescription
Idempotency-KeystringheaderA unique string of 1 to 255 printable ASCII characters for this create. Sending the same request again with the same key returns the first response, with the header `Idempotent-Replayed: true`, and creates nothing new. A key is remembered for 24 hours for your organization, this API key and this endpoint. The same key with a different body is refused with `422 IDEMPOTENCY_KEY_REUSED`. The same key while the first request still runs is refused with `409 IDEMPOTENCY_IN_PROGRESS`. A `5xx` or `429` response is not remembered, so a retry runs again. A replay of a response larger than 64 KB answers `409 IDEMPOTENCY_RESULT_TOO_LARGE`: read the resource with the get call.

Request Body

FieldTypeDescription
installer_id*stringYour own id for the person who installed. It is stored exactly as written. The first valid claim for an app and installer id wins.
app_idstringThe app. Optional for an SDK key tied to one app. Otherwise send app_id, app_bundle_id or app_package_name when the organization has more than one app.
app_bundle_idstringThe iOS bundle id, an alternative to app_id.
app_package_namestringThe Android package name, an alternative to app_id.
platform"ios" | "android"The platform of the install.
install_idstringThe install record from POST /attribution/match, when the install was matched by the SDK.
recovery_tokenstringThe referral token the invitation carried. A token alone is enough. An expired one is refused with RECOVERY_TOKEN_EXPIRED, a malformed one or one of another app with RECOVERY_TOKEN_INVALID. A genuine one issued in the last few minutes whose invitation has not arrived yet is answered 503 RECOVERY_TOKEN_PENDING with Retry-After: retry the same claim. When install_id is also sent, a refused or pending token falls back to the install.
link_idstringAn explicit referral link. A link that is not a referral link is refused with 409 LINK_NOT_REFERRAL and nothing is recorded.

Responses

200The result of the claim
FieldTypeDescription
status*"claimed" | "flagged" | "ignored" | "none"claimed and flagged are recorded. ignored is a later claim for an installer that already holds a credit. none means no referral link brought this install, and nothing is recorded.
referral_id*string | nullNull for none. For ignored, the id of the duplicate that was recorded.
link*oneOf
match*oneOf
flags*"outside_window"[]
duplicate_of*string | nullFor ignored, the referral that holds the credit.
updated*booleanTrue when this call replaced an earlier estimate with an exact match.
replay_state*string | nullreplayed when the token or device was already used by an earlier install.
400VALIDATION_ERROR for a body that does not fit, RECOVERY_TOKEN_INVALID for a malformed token or one of another app, or RECOVERY_TOKEN_EXPIRED for a token past its expiry or whose invitation is gone. A genuine token issued moments ago whose invitation has not arrived yet is a 503 RECOVERY_TOKEN_PENDING instead.
FieldTypeDescription
error*object
401Unauthorized. The key is missing or invalid.
FieldTypeDescription
error*object
403Forbidden. An API key needs the referrals:write scope and an SDK key needs attribution:write. KEY_APP_MISMATCH when an SDK key tied to one app names another app.
FieldTypeDescription
error*object
409LINK_NOT_REFERRAL when link_id is an ordinary link, or IDEMPOTENCY_IN_PROGRESS when a call with the same Idempotency-Key still runs.
FieldTypeDescription
error*object
422IDEMPOTENCY_KEY_REUSED. The same Idempotency-Key was sent with a different body.
FieldTypeDescription
error*object
429Rate limit exceeded
FieldTypeDescription
error*object
503Service temporarily unavailable, or RECOVERY_TOKEN_PENDING for a genuine token issued moments ago whose invitation has not arrived yet. Retry after the delay in the Retry-After header
FieldTypeDescription
error*object

Send installer_id, your own id for the new user, with one piece of evidence: an install_id from the SDK's attribution result, a recovery_token, or a link_id. The first valid claim for an app and installer id wins. Every outcome is a 200 with a status of claimed, flagged, ignored or none, and none records nothing. Send an Idempotency-Key to repeat a call safely.

An exact claim from an installer that holds an estimate upgrades the referral and returns updated: true. A link_id for a link that is not a referral link answers 409 LINK_NOT_REFERRAL. A 503 RECOVERY_TOKEN_PENDING carries a Retry-After header: send the same claim again after that many seconds.

List Referrals

GET/referrals

List referrals

Lists the organization's referrals, newest claim first. Each one is a credit that an invitation link earned for an install. Needs an API key with the referrals:read scope. SDK keys are refused with 403 SDK_KEY_ROUTE_FORBIDDEN. Page with pagination.next_cursor and send the same filters with it. A parameter this endpoint does not accept, or one repeated, is a 400 VALIDATION_ERROR. With format=csv the response is one CSV file of at most 10,000 rows (newest first) and a cursor is refused; X-Result-Truncated says whether the list was cut. The status filter uses the report status: duplicate and flagged are a referral's own state, and every other referral shows failed, retrying, delivered or claimed from its latest webhook delivery.

Parameters

NameTypeInDescription
installer_idstringqueryOnly referrals for this installer id, matched exactly as it was written.
link_idstringqueryOnly referrals credited to this link.
status"claimed" | "delivered" | "retrying" | "failed" | "flagged" | "duplicate"queryOnly referrals with this report status.
match"install_token" | "android_referrer" | "device_id" | "link_open" | "probabilistic"queryOnly referrals matched by this method.
platform"ios" | "android"queryOnly referrals from this platform.
app_idoneOfqueryOmit to read every app. A UUID reads one app. The literal "none" reads only referrals with no app.
fromstringqueryStart of the window on claimed_at, inclusive, as an ISO instant with an offset.
tostringqueryEnd of the window on claimed_at, exclusive, as an ISO instant with an offset.
cursorstringqueryThe pagination.next_cursor of the previous page. Not allowed with format=csv.
limitintegerqueryReferrals per page.
format"json" | "csv"querycsv downloads one file with the columns referral_id, status, installer_id, app_id, link_id, link_url, platform, match_method, match_exact, confidence, flags, claimed_via, tapped_at, installed_at, claimed_at and duplicate_of.

Responses

200One page of referrals, or the CSV file when format=csv
FieldTypeDescription
data*object[]
pagination*object
400VALIDATION_ERROR for an unknown, repeated or malformed parameter, or CURSOR_INVALID when the cursor does not fit this list.
FieldTypeDescription
error*object
401Unauthorized. The key is missing or invalid.
FieldTypeDescription
error*object
403Forbidden. The key needs the referrals:read scope, and an SDK key is refused with SDK_KEY_ROUTE_FORBIDDEN.
FieldTypeDescription
error*object
429Rate limit exceeded
FieldTypeDescription
error*object
503Service temporarily unavailable. Retry after the delay in the Retry-After header
FieldTypeDescription
error*object

Referrals come newest claim first. status is claimed, delivered, retrying, failed, flagged or duplicate: flagged and duplicate are the referral's own state, and the rest follow its latest webhook delivery. match.exact and evidence_class say how sure the match is. Page with pagination.next_cursor and send the same filters with it. format=csv returns one file of up to 10,000 rows and refuses a cursor.

Get a Referral

GET/referrals/{id}

Get a referral

Returns one referral with its claims, its webhook events with the payloads sent, and every delivery attempt. When an exact claim replaced an estimate, both claims and both events are listed. Needs an API key with the referrals:read scope. SDK keys are refused with 403 SDK_KEY_ROUTE_FORBIDDEN.

Parameters

NameTypeInDescription
id*stringpathThe referral id: ref_ followed by 32 hex characters, as the API returns it. A plain UUID is also accepted.

Responses

200The referral
400INVALID_ID when the id in the path is not a referral id.
FieldTypeDescription
error*object
401Unauthorized. The key is missing or invalid.
FieldTypeDescription
error*object
403Forbidden. The key needs the referrals:read scope.
FieldTypeDescription
error*object
404The referral does not exist in this organization
FieldTypeDescription
error*object
429Rate limit exceeded
FieldTypeDescription
error*object
503Service temporarily unavailable. Retry after the delay in the Retry-After header
FieldTypeDescription
error*object

The response adds the referral's claims, its webhook events with the payloads we sent, and every delivery attempt.

Resend a Referral's Events

POST/referrals/{id}/resend

Resend a referral's webhook events

Sends the newest webhook event of each endpoint again, with the same event id and payload, so a receiver can drop a repeat by id. Add endpoint_id to resend to one endpoint only. An event still being delivered, or finished more than 7 days ago, is listed in skipped. When nothing could be sent, the first refusal is returned: 409 CONFLICT for an event in flight or a referral with no events, 410 GONE for an event past the resend window. Needs the referrals:write scope, and the key's creator must be an owner or admin. Takes no body.

Parameters

NameTypeInDescription
id*stringpathThe referral id: ref_ followed by 32 hex characters, as the API returns it. A plain UUID is also accepted.
endpoint_idstringqueryResend only to this webhook endpoint.

Responses

202The events that were queued again and the ones that were skipped
FieldTypeDescription
resent*object[]
skipped*object[]
400INVALID_ID when the id in the path is not a referral id, or VALIDATION_ERROR when endpoint_id is not a UUID.
FieldTypeDescription
error*object
401Unauthorized. The key is missing or invalid.
FieldTypeDescription
error*object
403Forbidden. The key needs the referrals:write scope, and its creator must be an owner or admin.
FieldTypeDescription
error*object
404The referral does not exist in this organization
FieldTypeDescription
error*object
409CONFLICT. Every event is still being delivered, or the referral has no webhook events.
FieldTypeDescription
error*object
410GONE. Every event finished more than 7 days ago.
FieldTypeDescription
error*object
429Rate limit exceeded
FieldTypeDescription
error*object
503Service temporarily unavailable. Retry after the delay in the Retry-After header
FieldTypeDescription
error*object

Each endpoint receives its newest event again with the same event id, so a receiver can drop the repeat. Add endpoint_id to resend to one endpoint. An event still in flight, or finished more than 7 days ago, comes back in skipped.

Delete a Referral

DELETE/referrals/{id}

Delete a referral

Erases the referral with its claims, webhook events and delivery attempts. This cannot be undone. Ignored duplicates of the referral (the referrals that point to it as their duplicate_of) are erased with it. Needs an API key with the referrals:write scope.

Parameters

NameTypeInDescription
id*stringpathThe referral id: ref_ followed by 32 hex characters, as the API returns it. A plain UUID is also accepted.

Responses

204The referral is erased
400INVALID_ID when the id in the path is not a referral id.
FieldTypeDescription
error*object
401Unauthorized. The key is missing or invalid.
FieldTypeDescription
error*object
403Forbidden. The key needs the referrals:write scope.
FieldTypeDescription
error*object
404The referral does not exist in this organization
FieldTypeDescription
error*object
429Rate limit exceeded
FieldTypeDescription
error*object
503Service temporarily unavailable. Retry after the delay in the Retry-After header
FieldTypeDescription
error*object

Deleting erases the referral with its claims, webhook events and delivery attempts. It cannot be undone.