The referral conversion loop
The Referral programs guide walks the create-and-mint side of referrals: authoring a program, issuing per-contact codes, and the dashboard equivalents in Set up a referral campaign. This guide picks up where those stop, at the moment a referrer actually shares their link. It walks the read side of the loop end to end and shows the response bodies you get back at each step, the part of the API the reference pages name but never illustrate.1. The loop in one request flow
One referred signup moves through six requests:- Create the program.
POST /referrals/programsdefines the offer, including the per-side reward config (referrer_reward,referee_reward). Covered in Referral programs. - Mint a code per referrer.
POST /referrals/programs/:id/codesreturns the contact’s code and its public short link,https://api.orbit.devotel.io/r/AbCdEf23. - The prospect clicks. The public redirect counts the visit and 302s to your landing page with the code attached as a
ref_codequery parameter (section 2). - Your signup flow passes
ref_codeon. When your backend creates the new contact in Orbit, it includes the parameter, and Orbit records a conversion (section 3). - You read the funnel.
GET /referrals/programs/:id/conversionslists attributed signups;GET /referrals/programs/:id/leaderboardranks referrers (section 4). - You approve the payout.
POST /referrals/programs/:id/conversions/:conversionId/payflips the conversion topaidand fulfills both sides of the reward (section 5).
2. Watch the click land
GET /r/:code is public. It needs no API key because it is opened in a prospect’s browser, never by your server. Each hit bumps the code’s visit counter and answers with a redirect:
- The
ref_codequery parameter is the one your signup page actually sees.Locationpoints at your own domain, so hold the parameter through your signup form (a hidden field or a query passthrough) and forward it in the create-contact call in section 3. This is the attribution path that works when your signup flow lives outside Orbit. - The
ref_codecookie is scoped to the Orbit API origin. It only round-trips when a signup never leaves Orbit (a program with no landing URL configured); for an externaltarget_urlit never reaches your domain. Treat the query parameter as primary and the cookie as a fallback Orbit reads on its own origin.
3. Confirm the signup attribute
When your backend creates the referred contact, pass the code through with the sameref_code parameter your landing page received. Orbit matches it to the code, records a conversion, and leaves it at pending until you approve the reward:
reward_statusmovespending→paidon approval, or straight torejectedwhen an anti-abuse guard declines it (a signup from the referrer’s own IP, or a referrer and referee created minutes apart). Rejected rows carry areject_reasonand are never payable; the signup still counts toward the referrer’s funnel because the visit and signup genuinely happened.referrer_contact_id/referee_contact_idjoin the row back to your contacts;referrer_name/referee_nameare resolved display labels for rendering directly.converted_atis when the referred contact was created;rewarded_atis filled only when the payout lands.
status=pending for the approval queue, status=paid for reconciled payouts, and page with limit / offset (max 100 per page).
4. Read the leaderboard
GET /referrals/programs/:id/leaderboard ranks every issued code in the program by signups, then visits:
visitscounts 302s the redirect served for this code (section 2).signupscounts every recorded conversion for the code, including rejected ones: a referred signup that a fraud guard declined is still a signup for funnel purposes. Reward eligibility is tracked separately by each conversion’sreward_status.rewards_paidcounts conversions that reachedpaid, incremented only when a payout is approved. It is a count of paid rewards, not an amount; the amounts paid per side come from the program’s reward config (section 5).
limit defaults to 25), ordered so your strongest referrers rank first. For whole-program aggregates (total referrers, total signups, total rewards paid) call GET /referrals/programs/:id/totals instead; that is what the dashboard stat cards read, and it is not capped the way the leaderboard is.
5. Approve the payout end to end
Payouts are an operator decision: a conversion stayspending until you approve it. Approve with the pay endpoint:
paid with a rewarded_at timestamp, the referrer’s rewards_paid counter increments, and the fulfillments array carries one fulfillment record per rewarded side, so a two-sided program returns a referrer entry and a referee entry. What each fulfillment does depends on the program’s reward type:
creditapplies the configured amount per side, drawn from the reward pool your organization funds. The split comes from the program’sreferrer_rewardandreferee_rewardconfig, so a “10 for the referrer, 5 for the referee” offer pays exactly that on every approval.discount_codemints a redeemable code per side through the incentives engine; the minted code is returned in the response but kept out of durable logs, exactly because it is redeemable. Fulfillment providers for non-credit rewards are configured as described in the Incentives ledger guide.customrecords a tracked payout for your own fulfillment process and reports it back infulfillmentsso your system has a durable reference to reconcile against.
paid but surfaces the failure in structured logs on the platform side; the fulfillments statuses in the response tell you what actually delivered. Rejected conversions are never payable: the pay endpoint 404s on them, and the reject_reason stays on the row for your review.
6. Idempotency and re-tries
Every write in the loop is safe to replay, by design rather than by header:- Minting is mint-or-get.
POST /referrals/programs/:id/codeswith acontact_idthat already holds a code returns the existing code, so a nightly cohort job can call it for every member without checking first, and a retried mint never produces a second link for the same referrer. - Attribution deduplicates per referee. A conversion is unique per program and referee contact: if your signup webhook fires twice for the same referred signup, the second create-contact call does not record a second conversion.
- Pay is pending-only. The pay endpoint transitions
pending→paidand nothing else. If your client times out and retries, the retry finds no pending conversion and returns 404 instead of paying twice; the correct move after any pay timeout is toGETthe conversions list and readreward_statusrather than to re-issue the pay call blindly.
conversions, leaderboard) after any uncertain write, and a duplicated request collapses into the same result as a single one.
7. Related reading
- Referral programs — the create-and-mint side of this loop, driven through the API.
- Set up a referral campaign from the dashboard — the same workflow without code.
- Referral program model — the concepts behind programs, codes, and conversions.
- Incentives ledger — fulfillment providers for non-credit rewards.
- Referrals API reference — per-endpoint request and response contracts.