Affiliate Programs
Run an affiliate/partner program for your own product — MCP-native, affiliates enroll as orgs, no dashboards.
Affiliate Programs is a first-class realm your customers run for their products. The program owner is an org; each affiliate is also an org with its own PAT. Scoping is structural: admin tools (amba_affiliate_*) require the caller to manage the owner org; self-service tools (amba_affiliate_my_*) take no other-affiliate id and derive the affiliate from the caller's org — an affiliate literally has no parameter to read a peer's data.
The authoritative ledger is control-plane (programs, enrollments, conversions, the accrual ledger, settlements); the only tenant-side row is the in-app touchpoint, promoted to control on conversion so the payout path never reads a tenant DB.
Money-out is in preview (gated off). Everything that does not move money is live: program
management, enrollment, attribution (code and in-app link/touchpoint), commission accrual,
clawback, analytics, and notifications. The actual payout transfer (POST .../payouts and the
scheduled settlement) ships gated off behind ORG_PAYOUTS_LIVE and returns 503 PAYOUTS_NOT_ENABLED until a platform operator enables it after a customer-shoes test + counsel +
per-affiliate Connect onboarding. Build and dry-run freely; don't promise affiliates same-day cash
until your stack has the flag on.
Admin endpoints
| Method | Path | Description |
|---|---|---|
| POST | /v1/admin/affiliate/programs | Create a program (+ terms v1). |
| GET | /v1/admin/affiliate/programs | List programs your orgs (and descendants) own. |
| GET | /v1/admin/affiliate/programs/:programId | Get a program. |
| PATCH | /v1/admin/affiliate/programs/:programId | Update status / payout config / conversion definition / funding_source. |
| POST | /v1/admin/affiliate/programs/:programId/invite | Enroll an affiliate org (or guide an email to sign up). |
| GET | /v1/admin/affiliate/programs/:programId/affiliates | List affiliates + their conversion/commission totals. |
| GET | /v1/admin/affiliate/programs/:programId/affiliates/:enrollmentId | One affiliate's detail (codes, conversions, totals). |
| POST | /v1/admin/affiliate/programs/:programId/affiliates/:id/:action | approve / pause / ban an enrollment. |
| POST | /v1/admin/affiliate/programs/:programId/conversions | Report a conversion (idempotent; code optional — link-attributed if omitted). |
| GET | /v1/admin/affiliate/programs/:programId/conversions | List conversions. |
| POST | /v1/admin/affiliate/programs/:programId/conversions/:id/:action | approve / reject / clawback a conversion. |
| GET | /v1/admin/affiliate/programs/:programId/analytics | Program totals + clicks/EPC/conversion-rate + leaderboard. |
| GET | /v1/admin/affiliate/programs/:programId/leaderboard | Standalone affiliate leaderboard (commission + clicks + EPC). |
| GET | /v1/admin/affiliate/programs/:programId/payouts | Settlement (payout) history for the program's affiliates. |
| POST | /v1/admin/affiliate/programs/:programId/payouts | Run payouts (gated by ORG_PAYOUTS_LIVE). |
:action = clawback reverses a conversion's commissions (the underlying sale refunded/charged back): pre-payout commissions go reversed; an already-paid (or in-flight) commission becomes negative carry-forward debt that nets against the affiliate's next payout. Separately, a transfer.reversed Connect webhook (the affiliate's bank bounced a settled transfer) marks that settlement's commissions reversed and restores any carry-forward debt that payout had cleared.
Self-service endpoints (/v1/admin/affiliate-me)
Scoped entirely to the caller's own orgs. Financial reads require manage-level on the org.
| Method | Path | Description |
|---|---|---|
| GET | /v1/admin/affiliate-me/programs | Programs you're enrolled in. |
| GET | /v1/admin/affiliate-me/invites | Pending invitations for your org(s). |
| POST | /v1/admin/affiliate-me/invites/:enrollmentId/accept | Accept an invitation (activates + returns your code). |
| GET | /v1/admin/affiliate-me/programs/:programId/referral | Your code/link for a program. |
| POST | /v1/admin/affiliate-me/programs/:programId/referral | Mint a fresh code (rotates your active code). |
| GET | /v1/admin/affiliate-me/stats | Conversions + earnings across enrollments. |
| GET | /v1/admin/affiliate-me/conversions | Conversions across your enrollments. |
| GET | /v1/admin/affiliate-me/balance | Accrued-but-unpaid + paid, per currency. |
| GET | /v1/admin/affiliate-me/payouts | Settlement history. |
| POST | /v1/admin/affiliate-me/payout-request | What you're owed + payout-account readiness (no money moves). |
| POST | /v1/admin/affiliate-me/payout-account | Create a Stripe Connect payout account. |
| POST | /v1/admin/affiliate-me/payout-account/link | Hosted Connect onboarding link. |
| GET | /v1/admin/affiliate-me/payout-account | Onboarding status (manage-level). |
Client endpoint (in-app attribution)
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/client/affiliate/touchpoint | X-Api-Key | Record an in-app affiliate-link click / code-bind. |
Record a touchpoint when a visitor lands via an affiliate link — { code, source: "code"\|"link", anonymous_id, external_id? }. No session required (a pre-auth click). anonymous_id must be an unguessable client-generated id (e.g. a random device token) that you then report the conversion's subject_ref with; the endpoint is unauthenticated, so a touchpoint is advisory last/first-touch attribution — for trusted credit, report the conversion with the affiliate code (the authoritative path) rather than relying on code-less promotion. On a conversion reported with no code, the latest touchpoint for that subject (within the attribution window) is promoted into the control ledger; clicks also drive real EPC and conversion-rate in analytics.
Commission model
commission is a versioned JSONB tagged union (data, not columns) — enrollments are grandfathered to the version they activated under:
| Type | Shape | Notes |
|---|---|---|
percentage | { "type":"percentage", "bps":2000 } | 20% of gross. |
flat | { "type":"flat", "amount_cents":5000 } | Fixed per conversion. |
recurring | { "type":"recurring", "bps":2000, "max_cycles":12 } | Percentage for the first N billing cycles. |
tiered | { "type":"tiered", "bps":1000, "tiers":[{"min_conversions":10,"bps":2000}] } | Rate rises after N prior approved conversions. |
For multi-level programs, set multi_level_enabled: true and add a "multi_level":[{"level":1,"bps":500}] ladder. Upline overrides are double-gated: the per-program flag and the platform ORG_MULTI_LEVEL_LIVE server flag (MLM/pyramid regulatory exposure), so they don't accrue until the platform has been cleared for multi-level — the direct (level-0) commission is unaffected. platform_fee_bps is Amba's cut, deducted from each payout.
funding_source records the intended source of commission money: split_at_source (default — the attributed sale ran through Amba Payments) or external (the customer funds payouts another way, e.g. a pre-funded wallet). It is metadata today, not yet enforced by the rail: the (gated) transfer always moves from Amba's platform balance via the shared payOrg. Wiring source-specific settlement — e.g. for split_at_source, transferring from the customer's connected-account balance so Amba is truly pass-through — is part of the ORG_PAYOUTS_LIVE go-live work, pending the Stripe-mechanism + pricing decision with counsel.
Conversion definition + attribution
A program declares what counts (conversion_kind: event / payment / entitlement, with conversion_event_name / conversion_entitlement_id) and how credit is assigned (attribution_model: first_touch / last_touch, attribution_window_days). A reported conversion must carry the qualifying signal or it's rejected; first-touch credit lapses once the window expires. Conversions are idempotent per (program_id, project_id, external_id), so the same app-level order id can be reported to multiple programs independently. Self-referral is blocked at both enroll and conversion time unless allow_self_referral is set.
Notifications
On a commission being approved (accrued) or paid, the affiliate org's owner is emailed automatically — affiliates don't have to poll. Best-effort: a send failure never blocks accrual or a payout.
MCP tools
Admin: amba_affiliate_programs_{create,list,get,update}, amba_affiliate_invite, amba_affiliate_affiliates_{list,get}, amba_affiliate_affiliate_{approve,pause,ban}, amba_affiliate_conversions_list, amba_affiliate_conversion_{approve,reject,clawback}, amba_affiliate_analytics_get, amba_affiliate_leaderboard_get, amba_affiliate_payouts_list, amba_affiliate_payout_run. Self-service: amba_affiliate_my_{programs_list,invites_list,invite_accept,referral_get,referral_generate,stats_get,conversions_list,balance_get,payouts_list,payout_request,payout_create,payout_onboarding,payout_status}. Zero-to-enrolled: the public amba_affiliate_signup mints an org + PAT and enrolls in one browserless call. Conversions are reported server-to-server via the API, not an MCP tool.