Errors
API errors use standard HTTP status codes. Application-level failures also carry a stable
errorCode string so you can branch on the cause programmatically instead of parsing messages.
Response format
A failed request returns the matching HTTP status (including 400, 401, 404, 503) with this body:
{
"message": "Session expired",
"errorCode": "SESSION_EXPIRED",
"error": "Bad Request"
}Branch on errorCode, not message. Messages are human-readable and may be reworded;
errorCode values are stable.
Session
errorCode | HTTP | When |
|---|---|---|
SESSION_NOT_FOUND | 404 | The sid doesn't exist. |
SESSION_EXPIRED | 400 | The session passed its TTL. Create a new session. |
SESSION_NOT_OPEN | 400 | The session was cancelled and can no longer be used. |
Deposit addresses
errorCode | HTTP | When |
|---|---|---|
DEPOSIT_TRACKING_UNAVAILABLE | 503 | Tracking registration for the selected chain, or address bloom repair, could not be confirmed. No address is returned. Retry the same session, currency and chain; the saved address remains unchanged and registration retries durably. |
Do not show a previously cached address or QR code after this error. The widget provides a retry action. Missing provider configuration, rejected credentials, malformed provider responses and temporary provider failures all use this safe public error; provider response bodies and credentials are not exposed.
Admin deposit recovery
These codes belong to authenticated dashboard-admin actions, not operator-key endpoints. Refund request bodies and operator callback payloads are unchanged.
errorCode | HTTP | When |
|---|---|---|
REFUND_REJECTED | 400 | Eligibility/preparation failed before a chain write was attempted. |
REFUND_ALREADY_EXISTS | 400 | A live refund already owns the deposit; read its status instead of sending again. |
REFUND_UNAVAILABLE | 503 | Eligibility or claim persistence could not be verified. Reload the deposit and refund records before deciding another action. |
REFUND_RECONCILIATION_REQUIRED | 503 | A refund write was attempted or its final persistence/audit is uncertain. Do not resend, even if no hash is available. |
SWEEP_RECONCILIATION_REQUIRED | 503 | A sweep/refund claim blocks the action or sweep outcome needs verification. Do not reset the claim or force another transfer. |
PENDING means in-flight or unknown, not confirmed failure or proof of payout. Admin deposit detail additionally exposes sweepIntentStatus and sweepIntentId; SWEPT preserves the original aggregate ownership, gross/net/fee and receipt for finalize-only retries. Recovery is operations-gated; this release provides no automatic reconciliation/reset endpoint.
Withdrawals
A withdrawal is approved by your approval webhook (WITHDRAW_REQUEST). To
decline one, return an error with a numeric code — it maps to the errorCode the widget
surfaces to the user, all as HTTP 400:
Your numeric code | errorCode | Meaning |
|---|---|---|
1 | WITHDRAW_DECLINED | Generic decline (also the default if no code is given). |
2 | INSUFFICIENT_BALANCE | User lacks the balance for this amount. |
3 | WITHDRAW_UNAVAILABLE | Withdrawals temporarily unavailable. |
4 | USER_NOT_ELIGIBLE | User not permitted to withdraw. |
5 | DAILY_LIMIT_EXCEEDED | User hit their daily limit. |
6 | WITHDRAW_AMOUNT_TOO_LOW | Below your minimum. |
7 | WITHDRAW_AMOUNT_TOO_HIGH | Above your maximum. |
8 | ACCOUNT_VERIFICATION_REQUIRED | User must complete verification first. |
9 | WITHDRAW_SUSPENDED | Withdrawals suspended for this user. |
The API also returns one withdrawal error on its own, as HTTP 400:
errorCode | When |
|---|---|
WITHDRAW_IN_PROGRESS | A withdrawal for this session is already PENDING/PROCESSING. Wait for it to finish. |
Headless withdrawals
Errors specific to the headless (widgetless) flow. Supported assets are active Bankroll-registered EVM ERC-20 and Tron TRC20 tokens. Native assets (including TRX), malformed addresses and unsupported chains/tokens are rejected at intent creation or execute.
errorCode | HTTP | When |
|---|---|---|
HEADLESS_NOT_ENABLED | 403 | Headless is not enabled for your operator. (Deliberately generic — it does not reveal whether an intent exists.) |
OPERATOR_REFERENCE_REQUIRED | 400 | POST /session/withdraw with executionMode:"HEADLESS" omitted a non-empty operatorReference. |
HEADLESS_INVALID_RECIPIENT | 400 | Intent creation received the zero address, including its Tron Base58 representation. An address malformed for the selected chain instead fails request validation with HTTP 400 and no stable errorCode. Tron Base58 and 20-byte hex are accepted on Tron, not Base58 on EVM. |
HEADLESS_UNSUPPORTED_CHAIN | 400 / 409 | 400 at intent creation for an unsupported or inactive chain; 409 at execute if the locked chain is no longer supported. |
HEADLESS_UNSUPPORTED_TOKEN | 400 / 409 | 400 at intent creation for a native, inactive, or unregistered token; 409 at execute if the locked token is no longer supported. |
OPERATOR_REFERENCE_CONFLICT | 409 | You reused an operatorReference with a different immutable payload. The same reference + same payload is idempotent and returns the original intent. |
HEADLESS_INTENT_NOT_EXECUTABLE | 409 | Execute was called after the intent expired or was cancelled, or for a non-CREATED intent that has no existing reservation. A replay of an already-reserved intent returns its existing result instead. |
INSUFFICIENT_LIQUIDITY | 409 | The shared Bankroll balance minus active reservations cannot fund this payout. |
WITHDRAW_AMOUNT_TOO_HIGH | 400 | Headless execute exceeds the owning operator's configured single-withdrawal limit. Nothing is reserved or queued. |
DAILY_LIMIT_EXCEEDED | 400 | Headless execute exceeds the owner's rolling-24h daily limit, including active holds of any age and legacy in-flight payouts. Nothing is reserved or queued. |
WITHDRAW_CAP_UNVERIFIABLE | 409 | The operator limit, candidate amount or counted historical amount cannot be verified in the cap currency. Correct the configuration/data; nothing is reserved or queued. |
HEADLESS_CANCEL_NOT_ALLOWED | 409 | Cancel was attempted after broadcast uncertainty began, after a tx hash exists, or on a terminal intent. It can also mean concurrent resolution; read status and retry only if the intent is still cancellable. |
HEADLESS_RETRY_NOT_ALLOWED | 409 | Admin retry was called on a request that is not safely retryable (not WAITING_FOR_GAS / FAILED(retryable), terminal, or a tx hash already exists). |
HEADLESS_RETRY_IN_PROGRESS | 409 | A settlement worker already holds this request's job — a retry cannot pull it forward. |
PRICE_UNAVAILABLE | 503 | Execute could not resolve a fresh price; no reservation or job was created. |
BALANCE_UNAVAILABLE | 503 | Execute could not read Bankroll capacity; no reservation or job was created. |
The execute call fails closed rather than guessing when it cannot price or read availability — it returns HTTP 503 and reserves/queues nothing. Retry a 503 only once price/capacity is available again. Create and execute are idempotent only for the same immutable intent: reuse the same operatorReference and payload for create, and the same sessionRef for execute. Treat 409 as a state/configuration signal rather than blindly retrying it; inspect status or correct the underlying input first. HEADLESS_RETRY_* is the dashboard-admin recovery plane, not an operator-key endpoint.
For Tron, a send timeout, expired native transaction, missing receipt or RPC failure after broadcast uncertainty begins does not prove failure. The request remains RECONCILING, its reservation remains held, and cancel/re-sign/re-send is not allowed. Do not create a new operator reference to work around this state. A definite solid receipt and the configured confirmation depth resolve success or revert.
Transaction limits
errorCode | HTTP | When |
|---|---|---|
AMOUNT_BELOW_MINIMUM | 400 | Amount under the configured minimum for the operation. |
AMOUNT_ABOVE_MAXIMUM | 400 | Amount over the configured maximum for the operation. |
Authentication
| HTTP | When |
|---|---|
401 | Missing or invalid Authorization: Bearer <OPERATOR_SECRET_KEY> on an operator endpoint. |