API

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

ParameterTypeRequiredNotes
codestringyes1 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.

Response
{ "code": "ALPHA7", "valid": true }

POST/api/prep/referral/code

ParameterTypeRequiredNotes
accountIdstringyes0x and 64 hex characters.
codestringyesTrimmed 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.
Response on success
{
  "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:

reasonMeaning
formatThe code is not four to ten uppercase letters and digits.
reservedThe code is on the reserved list.
takenSomebody else holds it.
already_affiliateThis account already has a code. Read it rather than claiming another.
not_affiliateThe account is not a first-level affiliate, so the operator's key cannot issue it a code. It claims its own.
not_configuredThe operator has not bound a signing key on this deployment yet.
maxRebateRate is a server constant, clamped so the share given away can never exceed half the operator's fee. It is not read from the request body and no parameter raises it. How that share is divided between referrer and referee is not set per code at all: it comes from the operator configuration upstream, which only the operator's key can change.

POST/api/prep/referral/rate

ParameterTypeRequiredNotes
accountIdstringyes0x 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.
reasonMeaning
already_setThe account is on a rate. Returned with that rate, unchanged.
not_affiliateNo code on this account yet, so there is no rate to set.
not_configuredThe 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.

PathWhat it answers
/v1/referral/infoYour codes and their rates, plus the code that referred you. Its invitee count is a single combined figure.
/v1/referral/multi_level/statisticsInvites, 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_listWho joined under your code: address, when they bound it, what they have traded and what it paid you. Paged.
/v1/referral/multi_level/rebate_infoYour own code and the rates attached to it.
/v1/referral/multi_level/claim_codeIssue a code for this account. Upstream chooses it.
/v1/referral/edit_referral_codeRename your code. Allowed until somebody has joined with it.
/v1/referral/bindBind this account to somebody else's code.
/v1/referral/rebate_summaryWhat you have earned as a referrer.
/v1/referral/referee_infoThe code that referred you, if any.
/v1/referral/referee_rebate_summaryWhat you have earned as a referee.
/v1/referral/referral_historyRebates paid to you over time.
/v1/referral/referee_historyRebates 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.

A referee list names wallet addresses, and only to the referrer whose key signed the request. It is the one question an invite count cannot answer -- whether the person you sent the link to actually arrived. Nothing else about them is returned, and no endpoint here reads another account's referees.

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.