{"openapi":"3.1.0","info":{"title":"Referral API","version":"1.0.0","description":"Monthly limits reset at 00:00 UTC on the first day of each month. Each admitted request counts, including retries and later validation/work failures. Short-window 429 responses include Retry-After and X-RateLimit headers; honor them with exponential backoff and jitter while preserving the idempotency key. A temporary distributed-admission failure returns 503 and does not consume monthly usage. Click and server-capture requests use the separate referral-click allowance. Live uses the selected plan; test has separate Starter-sized allowances. Account exports are exempt from monthly usage. HTTP 402 USAGE_LIMIT_REACHED names the metric, usage, allowance and reset; PLAN_UPGRADE_REQUIRED means new live payout/recovery API writes or payout.marked_paid events require Growth or Scale. Starter retains manual/CSV reconciliation and reporting reads. See /docs for the tier matrix and durable export/CSV admission rules.","summary":"Referral links, attribution, commissions, and payout records."},"servers":[{"url":"https://referralapi.dev/api/v1"}],"security":[{"secretKey":[]}],"components":{"securitySchemes":{"secretKey":{"type":"http","scheme":"bearer"},"publicKey":{"type":"http","scheme":"bearer"}},"schemas":{"ErrorEnvelope":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"request_id":{"type":"string"}}}}},"Page":{"type":"object","required":["data","next_cursor","has_more"],"properties":{"data":{"type":"array","items":{}},"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}}}},"paths":{"/users":{"post":{"summary":"Create or sync a merchant user."},"get":{"summary":"List merchant users with bounded cursor pagination."}},"/users/{external_user_id}":{"get":{"summary":"Read one merchant user."},"patch":{"summary":"Update identity fields or signed_up_at."},"delete":{"summary":"Anonymize a merchant user."}},"/users/{external_user_id}/referral/enable":{"post":{"summary":"Enable referral capability for one user."}},"/users/{external_user_id}/referral-code":{"get":{"summary":"Read the user's current referral code."}},"/users/{external_user_id}/referral-code/change":{"post":{"summary":"Use the user's one successful code customization."}},"/users/{external_user_id}/referral/suspend":{"post":{"summary":"Suspend new referral activity for a referrer."}},"/users/{external_user_id}/referral/resume":{"post":{"summary":"Resume referral activity for a referrer."}},"/users/referrals/backfill":{"post":{"summary":"Provision referral codes for eligible existing users."}},"/users/{external_user_id}/referral-summary":{"get":{"summary":"Return code, counts, money, eligibility, and payout readiness for a merchant-hosted referrer dashboard."}},"/users/{external_user_id}/referrals":{"get":{"summary":"List referred customers for one referrer."}},"/users/{external_user_id}/commissions":{"get":{"summary":"List commissions for one referrer."}},"/users/{external_user_id}/payouts":{"get":{"summary":"List merchant-reported payouts for one referrer."}},"/users/{external_user_id}/referral-timeseries":{"get":{"summary":"Return daily or monthly signups, qualifications, approved earnings, reversals, and reported payout movements."}},"/attribution/click":{"post":{"security":[{"publicKey":[]}],"summary":"Capture browser referral attribution with a public key."}},"/attribution/server-capture":{"post":{"summary":"Capture referral attribution from a merchant server."}},"/referrals/claim":{"post":{"summary":"Claim stored attribution for a signed-in merchant user."}},"/events":{"post":{"summary":"Ingest user, subscription, payment, refund, correction, and payout-marked-paid events."}},"/referrals":{"get":{"summary":"List referral relationships for the environment."}},"/transactions":{"get":{"summary":"List payment transactions and normalized base amounts."}},"/commissions":{"get":{"summary":"List commission ledger entries."}},"/payouts":{"post":{"summary":"Record an external affiliate payout reported by the merchant."},"get":{"summary":"List merchant-reported payout records."}},"/payouts/{id}/reverse":{"post":{"summary":"Reverse a payout record without deleting history."}},"/recoveries":{"post":{"summary":"Record money recovered externally from an affiliate recovery balance."},"get":{"summary":"List affiliate recovery records."}},"/payout-reconciliation/exports":{"post":{"summary":"Generate a payout reconciliation CSV snapshot."}},"/payout-reconciliation/imports":{"post":{"summary":"Upload and validate a filled payout CSV."}},"/payout-reconciliation/imports/{id}/preview":{"get":{"summary":"Preview up to 50 validated CSV rows before confirmation.","parameters":[{"name":"after_row_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Pass next_row_id from the previous response to read the next page."}]}},"/payout-reconciliation/imports/{id}/confirm":{"post":{"summary":"Queue confirmation of valid payout CSV rows with durable progress.","description":"Returns 202 with confirmation_status=queued. Poll the preview or batches endpoint for confirmed_row_count, confirmation_status, and confirmation_error. Completed chunks remain recorded after a later failure; explicitly retry confirmation to resume unfinished rows. Repeated requests while queued or completed do not create additional payouts.","responses":{"200":{"description":"This import was already fully confirmed."},"202":{"description":"Confirmation queued; payout recording is still in progress."}}}},"/payout-reconciliation/batches":{"get":{"summary":"List payout reconciliation export and import batches."}},"/account-exports":{"post":{"summary":"Create a synchronous private JSON archive up to 10 MiB. Use account-export-requests for larger archives and recovery."}},"/account-export-requests":{"get":{"summary":"Read the latest export request in the authenticated account and mode. Returns data: null when none exists."},"post":{"summary":"Queue a private account archive. Reuse the same UUID after an uncertain response; a different active request returns 409.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["id"],"properties":{"id":{"type":"string","format":"uuid"}}}}}},"responses":{"202":{"description":"Durable request accepted or original request replayed. Poll status until completed, failed or expired."}}}},"/account-export-requests/{id}":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"summary":"Read export status, generation, stage, bytes_processed, created_at, expires_at and the completed checksum_sha256. Expiry follows the database clock."}},"/account-export-requests/{id}/retry":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"summary":"Resume a failed unexpired archive from saved bytes. Reuse the observed generation after an uncertain response; stale generations return current status without restarting work.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["generation"],"properties":{"generation":{"type":"integer","minimum":1,"maximum":2147483646}}}}}},"responses":{"200":{"description":"Current export status, with a new generation only for the first accepted retry."},"410":{"description":"Retention expired; create a new request UUID."}}}},"/account-export-requests/{id}/download":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"summary":"Verify completed storage and create a private download URL valid for at most 15 minutes, bounded by archive retention.","responses":{"200":{"description":"data contains id, download_url, download_url_expires_at, download_url_expires_in_seconds, size_bytes and checksum_sha256. Get a new URL after link expiry."},"409":{"description":"Archive is still being prepared."},"410":{"description":"Archive retention expired."},"503":{"description":"Storage verification or download signing is unavailable; retry later."}}}}}}