Skip to main content
Every error response (any non-2xx status) uses the same envelope:
status repeats the HTTP status. Switch on error.code — it is always present. The other fields are optional:
  • trace_id — 32-hex trace id, the same value as the X-Trace-Id response header. Quote it in support tickets.
  • retryAfterSec — on rate-limit and back-pressure rejects; also sent as the Retry-After header.
  • retryable — whether sending the same request again as-is can succeed. Absent means the server makes no claim.
  • details — endpoint-specific structured context.
A 5xx response never carries raw server error text: message is a generic sentence (or, for a few codes, a fixed explanation of what to do next), and details is omitted except for details.clientOrderId on some order-placement errors.

HTTP status code conventions

Business rejects vs HTTP errors

Every non-2xx response uses the error envelope above. A few 2xx responses also report a business outcome in the body: order placement (201, status: "REJECTED" + code), each entry of place-batch (success: false + code), a withdrawal request (201, status: "rejected" + code for a compliance refusal, or status: "REJECTED" with an empty id and the reason in reason, for example insufficient balance) and a single cancel (200, status: "not_found" / "already_terminal", or 202, status: "cancel_pending" for a cancel that is recorded and not complete yet, which you send again after Retry-After). Branch on the HTTP status first: on a 2xx, read the body’s status; on anything else, read error.code.

Order placement — full success-envelope shape with status: 'REJECTED'

POST /api/orders/place returns the same JSON shape for an accepted order and for a business reject — they share the schema and are distinguished by the top-level status field. (Request errors such as 400 invalid_request or 401 maker_mismatch use the standard error envelope instead.) The HTTP status is 201 Created even on reject because the request was accepted and processed; the body tells you the matcher’s verdict.
Branch on body.status: REJECTED carries the failure reason in code; any other value (PENDING, OPEN, FILLED, …) means the order was accepted. This response never reports PARTIAL: a partly filled resting order is OPEN with a non-zero filledQty.

Vault signature requests — standard error envelope

POST /api/vault/{split,merge,convert}-signature rejects — like any non-2xx response elsewhere — use the standard error envelope described at the top of this page: the outer status numeric mirrors the HTTP status, and the actual error code lives under error.code:
trace_id matches the X-Trace-Id header on the response — quote it when filing tickets. On 4xx rejects details carries endpoint-specific context (rate-limit windows, conflicting state, etc.); on 5xx it is omitted.

Upstream errors — flattened to error.code directly

Errors raised behind the API gateway (matching, vault signing, withdrawals) reach you with the original code surfaced at the top of the envelope:
Affects (non-exhaustive) POST /api/orders/place upstream rejects, POST /api/me/withdrawals/{id}/cancel (invalid_state, withdrawal_not_found) and POST /api/vault/{split,merge,convert}-signature rejects. Branch on error.code directly — the upstream code is lifted to the top level, so the /errors/codes catalogue is canonical wherever the rejection originated.
On these 4xx answers the original rejection is also attached, as error.details.details.upstream ({status, body}), for logging and diagnostics only — rely on top-level error.code exclusively. A 5xx never carries it (see the top of this page). When the original rejection has no machine-readable code (an HTML proxy page, an empty body, raw text), error.code is exchange_upstream_error and the raw answer sits in the same upstream wrapper.

Why two shapes

POST /api/orders/place and each entry of POST /api/orders/place-batch return the fill record even on a reject (zero fills, zero trades), so success and reject share one schema and the HTTP status stays 201. Vault signature endpoints have no fill record to return; they use the platform-wide HTTP error envelope. On a placement, code is present only when status is REJECTED. On an error response, the code is error.code; there is no top-level code. Branch on status for placements and on error.code for errors.

Error codes

Complete code reference across all endpoints.