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 (400, 401, 404) 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. |
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. Scope is EVM / ERC-20 only — non-EVM/native/Tron inputs 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. A malformed or non-EVM address instead fails request validation with HTTP 400 and no stable errorCode. |
HEADLESS_UNSUPPORTED_CHAIN | 400 / 409 | 400 at intent creation for an unsupported, inactive, or Tron 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. |
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.
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. |