Referrals
Verify a code, claim a code, read what a link has earned.
One public route, one operator route, and a signed family. What a code gives away is never a parameter anywhere here: it comes from the operator configuration, and no request body can raise it.
GET/api/prep/referral/verify
| Parameter | Type | Required | Notes |
|---|---|---|---|
| code | string | yes | 1 to 32 characters, letters, digits, underscore or hyphen. Anything else is invalid_request. |
Public and cheap, because it runs on every visit that arrives through a referral link and the answer is the same for everyone. Cached for five minutes.
{ "code": "ALPHA7", "valid": true }POST/api/prep/referral/code
| Parameter | Type | Required | Notes |
|---|---|---|---|
| accountId | string | yes | 0x and 64 hex characters. |
| code | string | yes | Trimmed and uppercased before it is checked, so casing is never the reason it fails. |
Issues a code for an account using the operator's key. This is not how a person gets a link: upstream accepts it only for accounts the operator has already promoted to first-level affiliates, and refuses every other account. Ordinary accounts claim their own code with their own key, through the relay, as described below.
The checks are the same either way: the code must match the allowed shape, must not be reserved, and must not already be taken.
- The shape is four to ten characters, uppercase letters and digits only. The code becomes a root-level URL, so nothing needing encoding is allowed.
- Reserved words are refused because the link would sit at a path the app owns or read as official: ADMIN, API, SWAP, EARN, FUTURES, ACCOUNT, BRIDGE, HELP, LOGIN, OFFLINE, DASHBOARD, SWAPMOS, SUPPORT, OFFICIAL, TEAM, STAFF, MOD, NULL, UNDEFINED.
{
"created": true,
"code": "ALPHA7",
"maxRebateRate": 0.4
}A refusal is also a 200, with created false and a reason, because none of these is an error a client should retry:
| reason | Meaning |
|---|---|
| format | The code is not four to ten uppercase letters and digits. |
| reserved | The code is on the reserved list. |
| taken | Somebody else holds it. |
| already_affiliate | This account already has a code. Read it rather than claiming another. |
| not_affiliate | The account is not a first-level affiliate, so the operator's key cannot issue it a code. It claims its own. |
| not_configured | The operator has not bound a signing key on this deployment yet. |
POST/api/prep/referral/rate
| Parameter | Type | Required | Notes |
|---|---|---|---|
| accountId | string | yes | 0x and 64 hex characters. |
Puts a newly issued link onto the rate every other link is on. A code a person claims for themselves is issued at a rebate rate of zero, and the rate is operator configuration they cannot reach, so without this a new link would be created successfully and pay its owner nothing.
- The rate is the server constant. There is no parameter for it and the body cannot influence it.
- It only ever raises a rate of zero. An account already on a rate is left alone, so this cannot undo a rate set deliberately, and calling it twice changes nothing.
- It only touches accounts upstream already recognises as first-level affiliates. It cannot create one.
| reason | Meaning |
|---|---|
| already_set | The account is on a rate. Returned with that rate, unchanged. |
| not_affiliate | No code on this account yet, so there is no rate to set. |
| not_configured | The operator has not bound a signing key on this deployment yet. |
Reading your own referral state
Signed, through POST /api/prep/relay. These are forwarded pass-throughs, so the field names are the upstream's.
| Path | What it answers |
|---|---|
| /v1/referral/info | Your codes and their rates, plus the code that referred you. Its invitee count is a single combined figure. |
| /v1/referral/multi_level/statistics | Invites, volume and rebates, split into the people you invited and the people they invited. Takes time_range: 1d, 7d, 30d or all_time. |
| /v1/referral/multi_level/referee_list | Who joined under your code: address, when they bound it, what they have traded and what it paid you. Paged. |
| /v1/referral/multi_level/rebate_info | Your own code and the rates attached to it. |
| /v1/referral/multi_level/claim_code | Issue a code for this account. Upstream chooses it. |
| /v1/referral/edit_referral_code | Rename your code. Allowed until somebody has joined with it. |
| /v1/referral/bind | Bind this account to somebody else's code. |
| /v1/referral/rebate_summary | What you have earned as a referrer. |
| /v1/referral/referee_info | The code that referred you, if any. |
| /v1/referral/referee_rebate_summary | What you have earned as a referee. |
| /v1/referral/referral_history | Rebates paid to you over time. |
| /v1/referral/referee_history | Rebates paid to you as a referee. |
From /v1/referral/info the app reads referrer_info.referral_codes -- an array, each entry with code, referrer_rebate_rate and referee_rebate_rate -- and referee_info.referer_code for the code that referred this account. Every count and volume on the screen comes from /v1/referral/multi_level/statistics instead: direct_invites, indirect_invites, direct_volume, indirect_volume, direct_rebate and indirect_rebate. The older endpoint has no direct-versus-indirect split, and reading one from it is how a referrer with two people under him came to be shown one of them and no volume.
Two more through the catalogue
GET /api/prep/op/referral.verify takes referral_code and is the same check as the route above, unshaped. GET /api/prep/op/referral.check takes referral_code and returns the code's details. Both are public and cached for a minute.