Application referral integration

Implement trusted referral capture, authenticated participant identity, canonical signup Events and retry-safe evidence in your application backend.

A working referral integration connects a browser arrival to an accepted backend Event. A script installation or an MCP connection alone does not establish a referral.

This reference describes the current source contract, reviewed on 3 October 2026. Start with a Sandbox Project and one signup flow. Confirm your environment's API, Assets and redirect URLs with its operator. The application's backend owns credentials, identity and evidence; the browser only transports a signed handoff.

Prepare the Project

Use an existing Project and referral Program with published attribution policy and reward terms. Register a SourceNamespace for your provider/account with POST /v1/sources. Keep the returned ID for Events from that source.

Backend requests use X-Api-Key with a Workspace-owned API key for the Project's environment. A Sandbox key cannot operate on a Live Project. Keep the key and Project signing secret in backend secret storage, outside browser bundles, chat prompts and logs.

The running API's GET /openapi describes current request schemas. Use that contract when building request bodies; legacy examples that submit an unsigned referral code are not this integration.

Choose the participant identity

Use { kind: "individual", externalId: "your-user-id" } for a person, or { kind: "group", externalId: "your-account-id" } for a group. The combination of Project, kind and external ID is unique. RefRef does not match participants by email or store group membership.

The backend obtains this ID from authenticated application state. An optional individual actor records who performed an action; it does not change attribution. A trusted enrollment or intake call resolves or creates the participant, so there is no separate identity registration call.

Enroll the participant using POST /v1/enrollments with projectId, participant and programId. Obtain or reuse their Program code with POST /v1/referral-codes; a vanity link uses POST /v1/referral-links and requires the existing code. Enrollment, sharing and conversion are separate operations.

Capture the arrival on the same origin

  1. A visitor follows a generated referral link through RefRef's redirect service. An accepted arrival includes a signed refref_handoff on the landing URL.
  2. Load /scripts/attribution.js from your configured Assets service. The transport first posts {} to your application's same-origin POST /refref/capture route, then posts { handoffToken } when a handoff is present.
  3. That route creates or verifies a server-owned context using a signed HttpOnly, SameSite=Lax cookie. Never adopt a browser-selected context ID. Verify the request Origin against your application origin.
  4. The backend creates a context through POST /v1/attribution/contexts, attaches the handoff through POST /v1/attribution/contexts/:contextId/touches, and reads its journal through GET /v1/attribution/contexts/:contextId?projectId=... when preparing evidence.
  5. After successful capture, the script removes only refref_handoff from the address. A failure preserves it for retry.

The browser transport needs Web Locks on HTTPS or a trustworthy localhost origin. Await capture before signup or an initial purchase:

await window.RefRefAttribution.capture();
// Only now submit the application signup through its trusted backend.

Wait for the script to load before calling it. Show a retry state when capture fails and prevent submission. Never interpret a transport failure as trusted proof of an empty journal. There is no unsigned referral-code cookie, hidden input contract or getCode() API.

Accept signup and preserve evidence

The authenticated backend sends POST /v1/track/events with the current schema's signup fields: Project, SourceNamespace, participant, type: "signup", a stable sourceEventId, occurredAt as an ISO 8601 UTC string with at most millisecond precision (for example, 2026-10-03T12:00:00.000Z), and the required attribution evidence. The backend signs authorization over the exact fact and context evidence using the Project signing secret. Do not fabricate a binding token or let the browser sign it.

Retain the first evidence snapshot and its journalRevision with the business operation in durable retry storage. If authorization expires, sign that same snapshot again. Do not read the latest browser journal on retry. The same business fact with conflicting evidence produces FACTS_CONFLICT rather than a reassignment.

Missing expected context can leave an Event pending. Display the returned processing state honestly; an HTTP acceptance response alone does not establish completed attribution. Evidence completion uses POST /v1/evidence/binding for the accepted fact. Never substitute a newly created Event to hide a pending result.

Add sharing UI

The Widget displays sharing links for an open enrollment in an active referral Program. Your backend issues its short-lived Widget JWT for the authenticated participant. Widget initialization can resolve the participant but does not create enrollment, signup or a referral. Show not_enrolled as a real state. It does not expose Referral or Reward history.

Extend to purchases only after signup works

A purchase Event supplies economic facts in purchase; intake derives its event identity from the paid invoice or order and uses paidAt. Do not send a separate purchase sourceEventId or occurredAt. Use verified backend billing facts, never browser-supplied amounts. Subscription renewals inherit their canonical Subscription origin without a fresh browser touch.

Where paid-history evidence is required, incomplete coverage remains pending until verified history is supplied. An earned Reward records an accounting result; it is not proof that a payment, discount or credit was delivered. Delivery requires a separate operating process.

Acceptance checklist

ExerciseInspect
A is enrolled and shares a link; B follows it in a separate sessionSuccessful capture, B's accepted signup, and the qualifying referral with distinct A/B participant IDs
Capture failsVisible retry state; no submission with invented empty evidence
Accepted response is lostRetry returns the same canonical result using retained business facts and first evidence
A later arrival occurs before retryThe old delivery retains its original journal revision
The browser bundle and requests are inspectedNo Workspace key or Project signing secret
Widget initializes before enrollmentExplicit not_enrolled, not an invented sharing link

The application's own test runner and browser journey must verify these outcomes. For tool-specific implementation prompts, use the Claude Code, Codex, or Lovable preview workflow.

On this page