Every error is JSON with the same shape and the right HTTP status. The code is stable and machine-readable — branch on it, never on the human message. details is optional and carries structured context (balances, amounts, validation issues).
No = a deterministic client error; retrying the identical request will fail the same way — fix the input. Yes = a transient condition (rate limit, upstream/on-chain rail unavailable); retry with backoff. For money writes, always retry with the same idempotency key so a retry can never double-apply.
auth/*
Code
HTTP
Meaning
Retryable
auth/missing_token
401
No bearer token supplied
No
auth/invalid_token
401
Key or session invalid, or out of scope
No
auth/expired_token
401
Session access token expired — refresh it
No
auth/method_not_allowed
401
That login method is not enabled for this app
No
auth/invalid_signature
401
SIWS signature did not verify
No
auth/challenge_expired
401
SIWS challenge too old — request a new one
No
auth/challenge_already_redeemed
401
SIWS challenge was already used
No
auth/invalid_init_data
401
Telegram initData did not verify
No
auth/init_data_stale
401
Telegram initData too old
No
auth/init_data_replayed
401
Telegram initData reused
No
auth/invalid_otp
401
Email code is wrong
No
auth/otp_not_found
401
No pending code for that email — initiate again
No
auth/otp_locked
401
Too many wrong attempts — locked out
No
user/*
Code
HTTP
Meaning
Retryable
user/not_found
404
No such user in this app/scope
No
user/suspended
403
The user is suspended
No
compliance/blocked
403
Blocked by a compliance rule
No
wallet/* & sign/*
Code
HTTP
Meaning
Retryable
wallet/not_found
404
No such wallet for this user/scope
No
wallet/insufficient_balance
409
Not enough balance for the operation
No
wallet/export_blocked
403
Export not allowed (policy or balance floor)
No
wallet/already_exported
409
Wallet was already exported
No
sign/invalid_transaction
400
The transaction could not be parsed/validated
No
sign/wallet_archived
409
Wallet is archived and cannot sign
No
sign/internal_failure
500
Signer failed unexpectedly
Yes
withdrawal/*
Code
HTTP
Meaning
Retryable
withdrawal/insufficient_balance
409
Not enough balance to withdraw
No
withdrawal/limit_exceeded
403
Crypto withdrawal disabled/limited for this app
No
ledger/*
Code
HTTP
Meaning
Retryable
ledger/session_not_found
404
Unknown or wrong-app session token
No
ledger/session_closed
409
Session is settling or already settled
No
ledger/insufficient_balance
409
Bet exceeds the session balance
No
ledger/bet_not_found
404
win references a bet that does not exist
No
ledger/bet_rolled_back
409
The bet arrived after its rollback
No
ledger/currency_mismatch
409
Operation currency differs from the session's
No
treasury & custody/*
Custodial treasury and pool-escrow custody share the custody/* namespace.
Code
HTTP
Meaning
Retryable
custody/wallet_not_found
404
No wallet for the user in this environment
No
custody/rail_unavailable
501
The on-chain money rail is not configured/available
Yes
custody/not_confirmed
503
Move is in flight — its on-chain fate is not yet settled
Yes
custody/insufficient_balance
409
User's real balance is too low for the move
No
custody/app_insufficient
409
The app treasury is too low to cover the payout
No
custody/pool_not_found
404
No such pool
No
custody/pool_currency_mismatch
409
Stake/pool currency does not match
No
custody/pool_settled
409
Pool is being or was already settled
No
custody/pool_refunded
409
Pool was already refunded
No
custody/lock_in_flight
409
A lock for this bet is already in progress
No
custody/conservation_violation
409
rake + payouts ≠ locked stakes — settle rejected
No
custody/ref_conflict
409
This ref was already used for a different move
No
custody/ref_voided
409
The referenced move was voided
No
referral/*
Code
HTTP
Meaning
Retryable
referral/user_not_found
404
No such user for the referral op
No
referral/code_not_found
404
No referral code matches
No
referral/code_collision
409
Generated code collided — retry generation
Yes
referral/cycle
409
Attribution would create a referral cycle
No
referral/insufficient_earnings
409
Withdrawal exceeds accrued earnings
No
validation, idempotency, ratelimit & server
Code
HTTP
Meaning
Retryable
validation/bad_request
400
Body/params failed validation (see details.issues)
No
idempotency/key_in_use
409
That idempotency key is mid-flight for a different request
Yes
ratelimit/exceeded
429
Too many requests — back off
Yes
server/internal
500
Unexpected server error
Yes
Idempotent operations (e.g. a duplicate ledger transactionUuid or a repeated idempotencyKey) are not errors — they return the stored original result, so a safe retry always converges.
These codes are returned by the endpoints in the API reference inside the standard error envelope.