Rebate Records
Query the list of invitees and rebate commission data through the following endpoints:
- Referral Status — View all referred users and binding status
- Referral Dashboard — View commission summary and downline counts
- Referral Logs — Paginated history of all referral operations (create, bind, unbind, claim)
- On-Chain Rebate Info — Query on-chain rebate data directly from smart contracts
- Traders / Affiliates / Claims — V2 endpoints documented below
Commission Calculation
When a referred trader (referee) executes a trade, the commission is calculated as:
commission = trade_fee × tier_commission_rate
Both maker and taker fees generate separate commission records if the respective trader has a referrer.
Earnings Status Model
Each referral_earnings row carries two independent status columns:
status — claim lifecycle
| Status | Description |
|---|---|
pending | Accrued, not yet claimed |
claimed | Redeemed on-chain — set when the backend observes the contract's RewardRedeemed event (claimed_at recorded) |
chain_sync_status — on-chain settlement sync
| Status | Description |
|---|---|
pending | Not yet batch-synced to the contract |
synced | Included in a successful batchSettleRewards transaction (chain_sync_tx recorded) |
failed | Sync failed after 3 attempts (chain_sync_error recorded) |
An earning is claimable only when chain_sync_status = 'synced' AND status = 'pending'.
Claim orders (commission_claims) have their own status: processing (created, awaiting on-chain redeem) → success (redeem confirmed, tx_hash/finished_at set). Failed claims carry a failure_reason and can be retried by admin.
Minimum Claim Amount
The minimum claim amount is 10 USDT. Claims below this threshold return a BELOW_MINIMUM error.
Batch On-Chain Sync
Pending referral earnings are batch-synced to the on-chain ZtdxRewardRouter contract every 5 minutes via batchSettleRewards. Once synced, earnings become claimable through POST /api/v1/referral/claim.
V2 Endpoints
All endpoints below require a JWT Bearer token.
List Traders
Lists the referrer's directly bound traders (is_affiliate = false) with per-trader volume and commission aggregates.
GET /api/v1/referral/traders
Authorization: Bearer <token>
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
biz | string | all | Business filter: all / spot / futures |
time | string | all | Time window for aggregates: all / 7d / 30d |
q | string | — | Substring filter on the trader address (optional) |
page | int | 1 | Page number (minimum 1) |
page_size | int | 20 | Items per page (1–100) |
Response
{
"items": [
{
"address": "0xabc...",
"bound_at": "2026-03-01T10:00:00Z",
"spot_volume": "120.00",
"futures_volume": "380.00",
"spot_commission": "12.00",
"futures_commission": "38.00",
"total_commission": "50.00",
"last_trade_at": "2026-04-15T09:00:00Z",
"status": "active"
}
],
"total": 8,
"page": 1,
"page_size": 20
}
| Field | Type | Description |
|---|---|---|
items[].address | string | Trader's wallet address |
items[].bound_at | string | When the trader bound to the referrer (ISO 8601) |
items[].spot_volume / futures_volume | string | Aggregated fee value attributed to this trader from the referrer's earnings records, per business line |
items[].spot_commission / futures_commission | string | Synced commission earned from this trader, per business line |
items[].total_commission | string | spot_commission + futures_commission |
items[].last_trade_at | string | null | Most recent earning-generating trade |
items[].status | string | active if the last trade is within 7 days, else inactive |
total | int64 | Total direct traders (not affected by filters) |
List Affiliates
Lists the referrer's downline affiliates (is_affiliate = true) with team and spread commission aggregates.
GET /api/v1/referral/affiliates
Authorization: Bearer <token>
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | int | 1 | Page number (minimum 1) |
page_size | int | 20 | Items per page (1–100) |
Response
{
"items": [
{
"address": "0xdef...",
"sub_rate": "0.05",
"spread_rate": "0.05",
"direct_traders_count": 3,
"team_volume": "500.00",
"spot_spread_commission": "5.00",
"futures_spread_commission": "20.00",
"total_spread_commission": "25.00",
"status": "active"
}
],
"total": 1,
"page": 1,
"page_size": 20
}
| Field | Type | Description |
|---|---|---|
items[].sub_rate | string | Rate passed down to the affiliate |
items[].spread_rate | string | Your spread on that affiliate's team: my_rebate_rate − sub_rate (floored at 0) |
items[].direct_traders_count | int64 | Number of the affiliate's own direct traders |
items[].team_volume | string | Aggregated fee value from the affiliate's team (spread-type earnings) |
items[].spot_spread_commission / futures_spread_commission | string | Synced spread commission per business line |
items[].total_spread_commission | string | Sum of the two spread commissions |
items[].status | string | Currently always "active" |
Promote a Trader to Affiliate
POST /api/v1/referral/affiliates
Authorization: Bearer <token>
Content-Type: application/json
{ "wallet": "0xdef...", "sub_rate": "0.05" }
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | Address of one of your direct traders |
sub_rate | Decimal | Yes | Rate passed to the affiliate; must satisfy 0 ≤ sub_rate ≤ your rebate_rate |
Response: { "ok": true }
| HTTP | Code | Description |
|---|---|---|
| 400 | SELF_ASSIGNMENT | Cannot promote yourself |
| 400 | SUB_RATE_EXCEEDS_OWN | sub_rate is negative or exceeds your own rebate rate |
| 400 | USER_NOT_FOUND | Target wallet is not a registered user |
| 400 | TARGET_NOT_AGENT | Target must first be an active tier-1 agent |
| 400 | TARGET_NOT_DIRECT_TRADER | Target wallet is not your direct trader |
| 400 | ALREADY_AFFILIATE | Target is already an affiliate |
Update Affiliate Sub-Rate
PUT /api/v1/referral/affiliates/:wallet
Authorization: Bearer <token>
Content-Type: application/json
{ "sub_rate": "0.06" }
Response: { "ok": true }
| HTTP | Code | Description |
|---|---|---|
| 400 | SUB_RATE_EXCEEDS_OWN | sub_rate is negative or exceeds your own rebate rate |
| 404 | AFFILIATE_NOT_FOUND | No affiliate relation with this wallet |
Claim Preview
GET /api/v1/referral/claims/preview
Authorization: Bearer <token>
{
"claimable": "45.00",
"pending": "12.50",
"last_claimed_at": "2026-04-10T08:15:00Z",
"claim_wallet": "0xabc..."
}
| Field | Type | Description |
|---|---|---|
claimable | string | Commission claimable now (chain_sync_status='synced' AND status='pending') |
pending | string | Commission awaiting on-chain sync (chain_sync_status='pending') |
last_claimed_at | string | null | Finish time of the latest successful claim |
claim_wallet | string | Wallet the claim would pay to (the authenticated address) |
List My Claims
GET /api/v1/referral/claims
Authorization: Bearer <token>
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | int | 1 | Page number (minimum 1) |
page_size | int | 20 | Items per page (1–100) |
Response
{
"items": [
{
"claim_no": "CLM-1a2b3c4d5e6f",
"amount": "45.00",
"status": "success",
"tx_hash": "0x...",
"failure_reason": null,
"applied_at": "2026-04-10T08:00:00Z",
"finished_at": "2026-04-10T08:15:00Z"
}
],
"total": 1,
"page": 1,
"page_size": 20
}
| Field | Type | Description |
|---|---|---|
items[].claim_no | string | Claim order number |
items[].amount | string | Claim amount (USDT, decimal string) |
items[].status | string | processing / success |
items[].tx_hash | string | null | On-chain redeem transaction hash |
items[].failure_reason | string | null | Failure detail, if any |
items[].applied_at | string | When the claim order was created |
items[].finished_at | string | null | When the on-chain redeem was confirmed |