Integrate Referral API
Use test keys first. Capture a referral click, sync the signed-in user, claim the attribution token, send a payment event, then read the referrer summary. Switch to live keys only after the full loop works.
Basics
- API base
https://referralapi.dev/api/v1- Browser SDK
https://referralapi.dev/sdk/v1.js- Authentication
- Public keys capture browser attribution. Secret keys stay server-side.
Quickstart
- Use test public and secret keys from the app.
- Add the browser SDK, or capture the code from your server with
POST /api/v1/attribution/server-capture. - On signup, create or sync the user with your immutable
external_user_idand originalsigned_up_at. - Claim the attribution token with
POST /api/v1/referrals/claim. - Send
payment.succeededfrom the server with a stableevent_id. - Render your in-app referral dashboard from
GET /api/v1/users/:external_user_id/referral-summary.
<script
src="https://referralapi.dev/sdk/v1.js"
data-key="rapi_pk_test_REPLACE_ME"
data-api-base="https://referralapi.dev"
></script>Server helper
The TypeScript helper defaults to https://referralapi.dev/api/v1. Use it only in server code. It rejects public keys so a secret key is not accidentally wired into browser JavaScript.
import { createReferralApiClient } from "@referralapi/sdk";
const referralApi = createReferralApiClient({
apiKey: process.env.REFERRAL_API_SECRET_KEY!,
});
await referralApi.users.create({
external_user_id: user.id,
name: user.name,
email: user.email,
signed_up_at: user.createdAt.toISOString(),
});
await referralApi.events.paymentSucceeded({
eventId: `payment.${payment.id}.succeeded`,
paymentId: payment.id,
externalUserId: user.id,
amount: payment.amount,
currency: payment.currency,
occurredAt: payment.createdAt.toISOString(),
});Referrer reports
Build referrer-facing screens inside your own authenticated product. Your server must verify the logged-in user owns the requested external_user_id before proxying these reports.
/referral-summaryreturns code, link, direct signups, qualified referrals, pending estimates, approved payable commission, reported payouts, recovery balance, and payout readiness./referrals,/commissions, and/payoutsare bounded cursor lists. Direct referrals are the default; passlevel=2orlevel=3only when you mean indirect results./referral-timeseriesreturns daily or monthly gross movements and reversals separately so your charts can reconcile to the ledger.
Money and payout records
Signup alone never earns commission. A payment creates pending commission, then approval waits for the saved customer-age rule, payment hold, FX normalization, and refund policy. Original payment currency and base reporting currency are both returned where they matter.
Referral API tracks what the merchant reports. It does not send affiliate money. Use POST /api/v1/payouts to record an external payout,POST /api/v1/payouts/:id/reverse to correct a mistake, and the CSV export/import flow for batch reconciliation. Uploads always require preview and confirmation before payout records are created. Confirmation returns an accepted request while a worker records valid rows. Poll the import preview for progress. If confirmation stops, completed rows stay recorded; retry to resume the remaining rows. Previews return up to 50 rows. Pass the returned next_row_idas after_row_id to read the next page. The dashboard uses private direct uploads for files up to 10 MB; inline API JSON requests remain bounded to 256 KiB including the CSV and its JSON envelope.
Referral API billing
Stripe-hosted Checkout and the Stripe Customer Portal manage your Referral API subscription. Billing affects live Referral API ingestion only; public docs, test-mode integration, read-only reports, and account exports stay available if an account is suspended.
Install with AI
Integrate referralapi.dev into this application.
Use docs at https://referralapi.dev/docs and API base https://referralapi.dev/api/v1.
Use the browser SDK with public key rapi_pk_test_REPLACE_ME.
Store the secret key only as REFERRAL_API_SECRET_KEY on the server.
Requirements:
1. Track ?ref= referral codes.
2. Create or sync users with immutable external_user_id and original signed_up_at.
3. Claim stored attribution after signup.
4. Send subscription, payment, refund, chargeback, correction and void events from the server.
5. Make event IDs stable and idempotent.
6. Render the user's referral dashboard from the reporting API only after authenticating that user in our app.
7. Never expose a Referral API secret key to client-side code.
8. If this app records affiliate payouts externally, report them with POST /api/v1/payouts using stable external_payout_id values.Account exports
Use a secret key to create an export with POST /api/v1/account-export-requests and a JSON body containing a UUID id. Keep that ID and reuse it if the response is lost. The archive preserves the account data from the time of the request.
Read GET /api/v1/account-export-requests/:id for progress, or omit the ID to recover the latest request after a restart. Status moves through queued and processing to completed, failed or expired. When failed, post the observed generation to /:id/retry to resume saved progress. Reuse the same generation after an uncertain retry response.
When completed, read /:id/download for a signed URL and SHA-256 checksum. The link lasts at most 15 minutes; request another link after it expires. The archive itself is available until expires_at. An expired archive requires a new request ID.
Creation and retry share six requests per hour. Status and download use the normal read limit. All export operations remain available independently of monthly usage and subscription state. The server SDK exposes these methods under accountExports. The older POST /api/v1/account-exports endpoint remains available for synchronous archives up to 10 MiB.
Errors and limits
Every response includes X-Request-ID. Collection endpoints default to 50 rows, reject limits above 100, and return opaque cursors. Report date ranges are bounded to 366 daily buckets or 60 monthly buckets. Reusing an event ID with the same payload is safe; reusing it with different data returns a conflict.
Monthly limits reset at 00:00 UTC on the first day of each month. Each admitted API request counts, including retries and requests that later fail validation or processing. Browser clicks and server capture use a separate referral-click allowance. Live uses your plan limits; test mode has separate Starter-sized allowances. Account exports stay available after quota exhaustion. View current totals in Billing.
HTTP 402 with USAGE_LIMIT_REACHED names the limit and reset time. With PLAN_UPGRADE_REQUIRED, the requested live payout API method needs Growth or Scale. Starter includes manual and CSV reconciliation and reporting reads. New payout/recovery API writes, payout reversals, and payout.marked_paid events use this method gate. Existing external-ID retries still receive replay or conflict results after a downgrade, within the request allowance. Test mode supports the full payout API. Short-window limits return HTTP 429 and are shared across the account’s keys within each mode. Every 429 includes Retry-After,X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Honor that delay, then retry with exponential backoff and jitter while preserving the original idempotency key. A temporary admission-provider failure returns HTTP 503; it never consumes monthly usage.
Sustained event/user limits are 60, 120, 300, 600, and 1,200 per minute from Starter through Scale 500,000. Referral capture limits are 120, 300, 600, 1,200, and 2,400; report limits are 30, 60, 120, 180, and 240. Each token bucket allows a two-minute burst. API concurrency is 5, 10, 25, 50, and 100. Test mode always uses the Starter profile.
User and referrer capacity does not reset monthly. Tracked users exclude anonymized users; active referrers are users currently enabled to refer. Automatic issuance needs room under both limits. If a create, enable, resume, or backfill exceeds capacity, the entire action is rejected with HTTP 402 USAGE_LIMIT_REACHED. Existing records, codes, and earned commissions are retained after a downgrade. Retry a rejected user event with its original ID and payload after restoring capacity.