Error Codes
Spot APIs return errors as {"error": "<STRING_CODE>"} plus the HTTP status. This page lists every code emitted by the spot subsystem grouped by category.
Auth & permission
| HTTP | error | When |
|---|---|---|
401 | (empty body) | Missing / invalid JWT, missing / wrong HMAC signature, or timestamp outside ±60000 ms of server time. |
403 | API Key permission denied: transfers not allowed | API-key caller hit POST /spot/transfer (JWT only). |
403 | API Key permission denied: withdraws not allowed | API-key caller hit POST /spot/withdraw/request (JWT only). |
403 | FORBIDDEN | Canceling an order that belongs to another user. Withdrawals are different: another user's withdrawal id returns 404 not found, never 403. |
Market state
| HTTP | error | When |
|---|---|---|
404 | MARKET_NOT_FOUND | symbol doesn't exist in spot_markets. |
404 | TICKER_NOT_FOUND | GET /spot/ticker/24hr?symbol=… for a symbol with no ticker row yet. Resolves after the first fill. |
409 | MARKET_HALTED | Market in halted status; no new orders. |
410 | MARKET_DELISTED | Market in delisted status; terminal. |
400 | market.status invalid | Market row has an unrecognized status value. |
Order placement (POST /spot/orders)
| HTTP | error | When |
|---|---|---|
400 | invalid side | side not in sell. |
400 | invalid type | type not in market. |
400 | invalid tif | tif not in post_only. Note: lowercase only. |
400 | price required for limit | limit order missing price. |
400 | quantity required for limit | limit order missing quantity. |
400 | quantity required for market sell | market SELL missing quantity. |
400 | quote_quantity required for market buy | market BUY missing quote_quantity. |
400 | INVALID_TICK | price is not a multiple of the market's tick_size. |
400 | INVALID_LOT | quantity is not a multiple of the market's lot_size. |
400 | BELOW_MIN_NOTIONAL | price * quantity < min_notional. |
400 | INSUFFICIENT_BALANCE | The lock the order needs (base for SELL, quote for BUY) exceeds caller's available. |
400 | POST_ONLY_REJECT | tif=post_only order would have crossed the book at placement. |
400 | SELF_TRADE | New order would cross caller's own resting order (DECLINE_TAKER policy). |
Order management
| HTTP | error | When |
|---|---|---|
400 | invalid id | Path :id is not a UUID. |
404 | ORDER_NOT_FOUND | Order doesn't exist, is terminal, or belongs to another user. |
404 | not found | Withdrawal id doesn't exist or belongs to another user. |
Engine availability
| HTTP | error | When | Action |
|---|---|---|---|
503 | spot trading disabled | SPOT_TRADING_ENABLED=false on this server. | Permanent for this env. |
503 | ENGINE_BUSY | Engine mpsc backlog full — POST /spot/orders and GET /spot/depth only. | Retry with backoff (200 / 400 / 800 ms). |
503 | ENGINE_RESTARTING | Recovery in progress — POST /spot/orders and GET /spot/depth only. | Retry after a few seconds. |
Engine overflow surfaces differently on the cancel endpoints: DELETE /spot/orders/:id maps it to 500 DB_ERROR, and DELETE /spot/orders (cancel-all) returns 200 with an empty canceled array — indistinguishable from having no open orders.
Transfer (POST /spot/transfer)
| HTTP | error | When |
|---|---|---|
400 | invalid direction: <direction> | direction not in spot_to_perp. |
400 | amount must be positive | amount <= 0. |
400 | insufficient balance | Source side does not have enough free USDT. |
400 | unsupported token: <token> | MVP supports USDT only. |
Withdraw (POST /spot/withdraw/request)
| HTTP | error | When |
|---|---|---|
400 | unsupported token: <token> | Token not in the spot wallet's supported list (currently DF). |
400 | amount below minimum <min> | amount below the server-configured minimum. No separate non-positive check exists — zero / negative amounts fail here too. |
400 | insufficient balance | available < amount for the requested token. |
409 | you have a pending withdrawal — submit it on-chain or wait for it to expire before signing a new one | A signed withdrawal at or after the contract's current nonce already exists. |
409 | withdrawal slot conflict — try again in a moment | The (user, chain, nonce) slot is occupied by a non-expired row. |
500 | chain query failed | Reading the vault's on-chain release nonce failed. |
500 | signer unavailable | The backend withdraw signer could not be initialized. |
500 | sign failed | EIP-712 signing failed. |
503 | spot blockchain not initialized | The BSC blockchain service is not running. |
Admin (/admin/spot/*)
| HTTP | error | When |
|---|---|---|
400 | INVALID_STATUS | status not in delisted. |
400 | AMOUNT_NON_POSITIVE | Credit amount is <= 0. |
404 | DISABLED | POST /admin/spot/balances/credit called when TESTNET_ONLY != true. |
409 | MARKET_EXISTS | Creating a market whose id already exists. |
500 | DB_ERROR | Postgres error during the admin write. |
Server faults
| HTTP | error | When |
|---|---|---|
500 | DB_ERROR | Unexpected Postgres error on the trading endpoints (orders / trades / market data). Investigate logs. |
500 | internal error / internal | Unexpected Postgres error on the wallet-side endpoints (/spot/transfer, /spot/withdraw*, /spot/withdrawals*). These endpoints never return DB_ERROR. |
500 | (html body) | Panic in the request task. Capture the request and report. |