Skip to content

Affiliates

The partner program as your back office reads it, the sponsor tree, referrals and the upline they are credited under, the customers each affiliate brought in, and the program's settings.

An organization that sells on its Cuvo storefront can run a partner program: affiliates share a link or a coupon code, a customer who buys through one is referred to that affiliate, and affiliates recruit other affiliates beneath them. These endpoints let your own system read all of it, so a back office, a commission engine or a partner portal can be built on the same records the storefront credits sales against.

Everything here needs affiliates:read, which an organization's administrator grants on its own. It is read-only: creating and editing affiliates comes in a later release, under affiliates:write.

OperationWhat it answers
GET /v1/affiliatesAffiliates, newest first
GET /v1/affiliates/{id}One affiliate, with its links and coupon codes
GET /v1/affiliates/{id}/customersThe customers one affiliate referred, as an affiliate may see them
GET /v1/affiliate_referralsReferrals, newest first
GET /v1/affiliate_referrals/{id}One referral, with its upline and acquisition
GET /v1/affiliate_programThe program's settings, or 404 when the organization runs none

Every list takes limit and starting_after and answers { data, has_more }; see Pagination. Lists also take created_after, and the affiliate and referral lists take updated_after: pass back the newest updated_at you have seen to poll for what moved.

Affiliates and the tree

{
  "id": "aff_test_jordan",
  "name": "Jordan Lee",
  "email": "jordan@example.com",
  "kind": "individual",
  "status": "approved",
  "sponsor": "aff_test_avery",
  "depth": 1,
  "ref_code": "JORDAN",
  "referral_url": "https://shop.example.com/r/JORDAN",
  "links": [{ "id": "lnk_test_jordan", "slug": "jordan", "label": "Main link", "url": "https://shop.example.com/r/jordan", "destination_path": null, "archived_at": null }],
  "coupon_codes": [{ "code": "JORDAN15", "active": true, "percent_off_bps": 1500, "fixed_off_cents": null }],
  "external_id": null,
  "metadata": {}
}
  • sponsor is the affiliate who recruited this one, and depth how many levels below the top of the tree it sits: 0 has no sponsor. Filter by sponsor to walk the tree one level at a time.
  • kind is individual for a person and corporate for a business (a med spa, a practice), whose own sales earn the program's corporate reward rows.
  • Only an approved affiliate is credited. pending waits for approval or for the onboarding steps the program requires; onboarding records when each was completed and which version of the terms and agreement was signed.
  • referral_url and a link's url are null while the organization has no storefront domain to build them on.
  • external_id and metadata are your own key and values for the affiliate. email filters an exact address without regard to case; ref_code and external_id filter exactly.

An affiliate never carries money. Commission totals, reward overrides, payout details and the clinic's private notes are not part of this API.

Referrals, the upline and the acquisition

A referral is one customer credited to one affiliate, by their tracked link (attributed_via: "cookie") or their coupon code at checkout ("coupon").

{
  "id": "afr_8Qm2",
  "affiliate": "aff_4kT9",
  "customer": "cus_51Hk",
  "patient": "pat_7d3a",
  "attributed_via": "coupon",
  "coupon_code": "GLOW20",
  "upline": { "l2": "aff_2bW1", "l3": "aff_9xQe", "frozen_at": "2026-11-05T14:02:11Z" },
  "acquisition": { "kind": "affiliate", "affiliate": "aff_4kT9", "decided_at": "2026-11-05T14:02:11Z" },
  "created_at": "2026-11-05T14:00:03Z",
  "updated_at": "2026-11-05T14:02:11Z"
}

The upline is frozen; the sponsor is live. upline.l2 and upline.l3 are the affiliate's sponsor and that sponsor's sponsor at the moment the referral was first credited, and frozen_at is that moment. An organization can move an affiliate to a different sponsor later; the affiliate's sponsor changes, the referrals it already holds do not. A commission on this customer walks the frozen upline, so build tree payouts from upline, never from re-reading the affiliates' current sponsors.

The acquisition decides who is credited. A customer can arrive through an in-house affiliate and through an outside tracking network at once. At their first purchase the storefront decides who acquired them, and acquisition records it: kind is affiliate (with that affiliate), network or none. While acquisition is null the referral's own affiliate is the one credited; once it is set, it decides, and a referral whose acquisition went to a network credits no affiliate at all.

customer is null until the referred person holds a store account, and patient until they have a chart. Each change to a referral (the upline frozen, the customer known, the acquisition decided) moves updated_at and is announced as affiliate_referral.updated.v1.

An affiliate's customers

GET /v1/affiliates/{id}/customers is the projection meant for affiliate-facing systems: a partner portal or a payout statement. One row per referral, newest first, paged by referral:

{
  "customer": "cus_51Hk",
  "referral": "afr_8Qm2",
  "referred_at": "2026-11-05T14:00:03Z",
  "attributed_via": "coupon",
  "first_order_at": "2026-11-05T14:02:11Z",
  "last_order_at": "2026-11-05T14:02:11Z",
  "order_count": 1,
  "active": true,
  "label": null
}

active means a paid purchase in the last 90 days or an active membership; filter on it with active=true or active=false. There is never an email, a phone number, a date of birth, an address, a product or anything clinical on this row.

Disclosure. label names the customer only as far as the program's customer_disclosure allows, and the default discloses nothing:

customer_disclosurelabel
ids_only (the default)null
initialsInitials only
first_name_last_initialA first name and a last initial

The organization sets this on its program. A system that shows customers to affiliates should render label when it is present and the customer id when it is not.

The program

GET /v1/affiliate_program reads the organization's settings: whether the program is active, the cookie window, the holding period before a commission can be paid, how many levels of the tree a sale pays, whether new affiliates are approved automatically, and which onboarding steps are required. commission_engine says who pays the affiliates: internal is Cuvo's own commission ledger, and external means the organization runs commissions in its own system, named with a link in external_engine. Under external, attribution carries on exactly as before, and Cuvo posts no new commission lines.

Events

affiliate.created.v1, affiliate.updated.v1, affiliate_referral.created.v1 and affiliate_referral.updated.v1 ride the existing feed and endpoints, and are delivered only to a grant holding affiliates:read. Each carries the resource's id and never the affiliate's or the customer's details: read the resource by that id. See Events and realtime.

In test mode

Every sandbox starts with the same program and the same three-level tree, so you can build tree logic before a real partner exists:

AffiliateKindSponsorCoupon code
aff_test_averyindividualnoneAVERY10, 10% off
aff_test_jordanindividualaff_test_averyJORDAN15, 15% off
aff_test_harborcorporateaff_test_jordanHARBOR20, $20 off

The sandbox program is active, uses the internal engine, discloses ids_only, requires no onboarding step and approves affiliates on sign-up.

POST /v1/test/affiliates adds one of your own, with a link and a 10% coupon code, under any affiliate in the tree, and announces it as affiliate.created.v1. It needs test:manage and an Idempotency-Key; status defaults to approved.

curl -X POST https://api.cuvo.co/v1/test/affiliates \
  -H "Authorization: Bearer cuvo_sk_test_…" \
  -H "Idempotency-Key: affiliate-riley-1" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Riley Park", "email": "riley@example.org", "sponsor": "aff_test_harbor" }'

Lists answer your own affiliates newest first, then the tree. Every affiliate you add is an individual, and an email is used once across the sandbox, as it is in an organization.

Referrals in the sandbox come from test purchases: a purchase that names an affiliate opens the referral that credits it. Its upline is the affiliate's sponsor chain, frozen when the referral was opened; no acquisition is decided, since the sandbox has no outside network. In an affiliate's customers, each referral reports the test purchase that opened it: one order, at the referral's creation, and active. POST /v1/test/reset removes your affiliates and referrals; the tree stays.