Errors

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

errorCodeHTTPWhen
SESSION_NOT_FOUND404The sid doesn't exist.
SESSION_EXPIRED400The session passed its TTL. Create a new session.
SESSION_NOT_OPEN400The session was cancelled and can no longer be used.

Deposit addresses

errorCodeHTTPWhen
DEPOSIT_TRACKING_UNAVAILABLE503Tracking 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.

errorCodeHTTPWhen
REFUND_REJECTED400Eligibility/preparation failed before a chain write was attempted.
REFUND_ALREADY_EXISTS400A live refund already owns the deposit; read its status instead of sending again.
REFUND_UNAVAILABLE503Eligibility or claim persistence could not be verified. Reload the deposit and refund records before deciding another action.
REFUND_RECONCILIATION_REQUIRED503A refund write was attempted or its final persistence/audit is uncertain. Do not resend, even if no hash is available.
SWEEP_RECONCILIATION_REQUIRED503A 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 codeerrorCodeMeaning
1WITHDRAW_DECLINEDGeneric decline (also the default if no code is given).
2INSUFFICIENT_BALANCEUser lacks the balance for this amount.
3WITHDRAW_UNAVAILABLEWithdrawals temporarily unavailable.
4USER_NOT_ELIGIBLEUser not permitted to withdraw.
5DAILY_LIMIT_EXCEEDEDUser hit their daily limit.
6WITHDRAW_AMOUNT_TOO_LOWBelow your minimum.
7WITHDRAW_AMOUNT_TOO_HIGHAbove your maximum.
8ACCOUNT_VERIFICATION_REQUIREDUser must complete verification first.
9WITHDRAW_SUSPENDEDWithdrawals suspended for this user.

The API also returns one withdrawal error on its own, as HTTP 400:

errorCodeWhen
WITHDRAW_IN_PROGRESSA 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.

errorCodeHTTPWhen
HEADLESS_NOT_ENABLED403Headless is not enabled for your operator. (Deliberately generic — it does not reveal whether an intent exists.)
OPERATOR_REFERENCE_REQUIRED400POST /session/withdraw with executionMode:"HEADLESS" omitted a non-empty operatorReference.
HEADLESS_INVALID_RECIPIENT400Intent 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_CHAIN400 / 409400 at intent creation for an unsupported or inactive chain; 409 at execute if the locked chain is no longer supported.
HEADLESS_UNSUPPORTED_TOKEN400 / 409400 at intent creation for a native, inactive, or unregistered token; 409 at execute if the locked token is no longer supported.
OPERATOR_REFERENCE_CONFLICT409You reused an operatorReference with a different immutable payload. The same reference + same payload is idempotent and returns the original intent.
HEADLESS_INTENT_NOT_EXECUTABLE409Execute 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_LIQUIDITY409The shared Bankroll balance minus active reservations cannot fund this payout.
WITHDRAW_AMOUNT_TOO_HIGH400Headless execute exceeds the owning operator's configured single-withdrawal limit. Nothing is reserved or queued.
DAILY_LIMIT_EXCEEDED400Headless 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_UNVERIFIABLE409The 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_ALLOWED409Cancel 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_ALLOWED409Admin 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_PROGRESS409A settlement worker already holds this request's job — a retry cannot pull it forward.
PRICE_UNAVAILABLE503Execute could not resolve a fresh price; no reservation or job was created.
BALANCE_UNAVAILABLE503Execute 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

errorCodeHTTPWhen
AMOUNT_BELOW_MINIMUM400Amount under the configured minimum for the operation.
AMOUNT_ABOVE_MAXIMUM400Amount over the configured maximum for the operation.

Authentication

HTTPWhen
401Missing or invalid Authorization: Bearer <OPERATOR_SECRET_KEY> on an operator endpoint.