Affiliates
Package affiliates is a partner program that pays commission on what your referrals spend.
Package affiliates is a partner program that pays commission on what your referrals spend.
| Base URL | https://api.hanzo.ai |
| Operations | 10 |
| Auth | Authorization: Bearer $HANZO_API_KEY |
affiliates
POST /v1/affiliates/apply
Enrolls the caller's OWN org as an affiliate at status applied,
optionally requesting a vanity code, and answers the record — 201 on the first
apply, 200 with created:false afterwards.
IDEMPOTENT, first apply wins: one affiliate per org, so re-applying never creates a second row and never resets an existing approval. Applying is not joining — no code is minted and nothing accrues until staff approve, which is where both the code and the commission rate come from.
The org is the validated caller's, never a field. A malformed vanity code is refused up front; the code is only REQUESTED here, and approval may mint a different one if the requested code is taken.
Request body — application/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
requestedCode | string | — | RequestedCode is the vanity code the applicant asks for; approval may mint a different one if it is taken. |
POST /v1/affiliates/attribute
Records the first-touch edge every later commission is computed from: the caller's org was referred by the affiliate that owns this code.
The REFERRED org is the validated caller, never a field. A caller that could name the referred org could attach itself to somebody else's revenue. The affiliate is resolved from the code, and only an APPROVED affiliate's code resolves.
FIRST TOUCH WINS, set once: one affiliate per referred org, so a re-post
answers the existing edge with created:false rather than moving the
attribution. Self-attribution is refused, and so is a code that would make a
cycle in the upline chain. An unknown code is a 404, deliberately: an
affiliate code IS a public shareable link, so whether one is real is public by
design, and the caller legitimately needs to know its link resolved.
A user-level mirror of the edge is written best-effort; a conflict there never fails the org attribution, which is the money-bearing one.
Request body — application/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
code | string | — | Code is the affiliate code the referred org arrived with. |
POST /v1/affiliates/click
Counts a click on a share link. PUBLIC — it takes no principal, because a visitor clicking a shareable link has no session yet.
The ping folds into an in-memory buffer and NEVER writes the money database synchronously, so a click flood cannot contend with the accrual and payout write path; tallies are flushed in one batch on the next authenticated links read and at shutdown. Clicks are a vanity metric: no accrual and no payout ever reads them — those key on real metered spend — so click inflation cannot move money.
Any well-formed code is accepted WITHOUT checking that it exists,
deliberately: this is not a code-existence oracle. counted reports that the
buffer took the ping, not that the code is real; an unknown code simply no-ops
at flush time.
Request body — application/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
code | string | — | Code is the share-link code that was clicked. |
GET /v1/affiliates/leaderboard
Answers the top affiliates by lifetime accrued commission, shown by OPT-IN HANDLE with aggregate figures only, plus the caller's own exact rank.
It never discloses an org identity and never a referred org's usage. An affiliate that has set no handle still OCCUPIES its rank but is not listed — so opting out hides the name, not the position, and the visible board must not be read as a complete roster.
The caller's own row carries its exact GLOBAL rank, computed over the whole approved set rather than over the page, so it is right well outside the top of the board. Only an approved affiliate has a rank. Requires a validated principal; a signed-in non-affiliate may read the board but gets no personal row.
GET /v1/affiliates/me/earnings
Answers the caller's own commission ledger: per period, the margin it earned against and the commission taken from that margin; and per referred org, that referral's aggregate contribution. Integer cents throughout.
The per-org view deliberately carries the affiliate's OWN earned share and NOT the referred org's spend or margin. An affiliate is entitled to what it earned, not to a restatement of its customer's usage — the period view is where the margin base appears, aggregated across every referral.
Scoped server-side to the validated caller's affiliate; a caller that is not
one gets isAffiliate:false.
POST /v1/affiliates/me/handle
Sets the caller's public leaderboard display name, or clears it.
The handle IS the opt-in. An empty handle opts out: the affiliate keeps its rank and can still see its own row, it simply stops being listed to anyone else. That is the whole privacy control — there is no separate visibility flag, and no way to be listed without choosing a name.
Requires a validated principal and an existing affiliate record; apply first. The handle is bounded and restricted to letters, digits, space, hyphen, underscore and dot.
Request body — application/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
handle | string | — | Handle is the public leaderboard display name; empty opts out. |
GET /v1/affiliates/me/links
Answers the caller's share links, each with its URL and its funnel: clicks tracked, signups — orgs attributed with that code — and conversions, meaning how many of those signups have actually produced commission.
Signups and conversions are DERIVED from the commission ledger and never stored, so they cannot drift from the money. Clicks are the one stored counter and the one that is pure vanity.
Any pending public click pings are folded into the store before the read, in
one batch — which is how the counters stay current without a database write
per click. Scoped to the validated caller's own affiliate; a non-affiliate
gets isAffiliate:false and the link cap.
POST /v1/affiliates/me/links
Mints a new share link for the caller's own affiliate and answers it with its full URL, 201.
APPROVAL IS REQUIRED: an org that has applied but is not approved is refused, because a link that cannot accrue is a link that quietly loses the referral. A requested vanity code must be valid and free across the WHOLE directory — codes are one global namespace, so a taken code is a 409 rather than a silent alias. Omit the code and a random one is minted.
Bounded per affiliate. The label is cosmetic: it is trimmed, stripped of control characters and capped, and it is never part of a code.
Request body — application/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
code | string | — | Code is an optional vanity code; it must be free across the whole directory, and omitting it mints a random one. |
label | string | — | Label is cosmetic — trimmed, stripped of control characters, capped — and never part of a code. |
GET /v1/affiliates/me
Answers the richer self-view: the same lifetime accrued, pending and paid commission and payout history, plus the caller's downline broken out by upline LEVEL — direct, second, third — each with the rate paid at that level and how many orgs sit there.
Commission is MULTI-LEVEL: a referred org's spend pays up its referral chain,
three levels deep and no further. The direct level is the affiliate's own
negotiated rate; the second and third are platform-wide switches, read live,
so the schedule shown is the one actually in force rather than one compiled
in. A caller that has not applied still gets that schedule alongside
isAffiliate:false, so the console can show what it would earn.
Scoped to the validated org and nothing else, and refused without a principal. A PURE READ — it reports the downline but accrues nothing.
GET /v1/affiliates
Answers the caller org's OWN affiliate standing: status, referral code and share link, commission rate, how many orgs it has referred, and its lifetime accrued, still-pending and already-paid commission in integer cents, with its payout history.
An org that never applied gets an honest isAffiliate:false and the default
rate rather than a 404 — the console renders the apply form off that answer.
The affiliate is resolved from the VALIDATED org, never from a field, so this can only ever read the caller's own row; without a principal it is refused. It is a PURE READ: nothing accrues until the sweep runs. Commission is earned on Hanzo's MARGIN, never on the referred customer's bill, so nothing here changes what that customer pays.
How is this guide?