Skip to main content
All error responses share the same envelope:

HTTP status code conventions

Business rejects vs HTTP errors

Two reject-envelope shapes are in use across the partner surface; which one you get depends on the endpoint family. Branch on the path, not on a single shape detector.

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

POST /api/orders/place always returns the same JSON shape regardless of outcome — placement success and business reject share the schema, distinguished by the top-level status field. 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: OPEN / FILLED / PARTIAL are good, REJECTED carries the failure reason in code.

Vault signature requests — bare {code, message} envelope

POST /api/vault/{split,merge,convert}-signature (and most other non-/orders/place endpoints when they reject through the core-api → exchange-service hop) return a doubly-nested envelope: the outer status numeric mirrors the HTTP status, and the actual error code lives under error.code:
trace_id is always present and matches the X-Trace-Id header on the response — quote it when filing tickets. details is endpoint- specific (rate-limit windows, conflicting state, etc.).

Upstream errors — flattened to error.code directly

Errors raised by downstream services (matcher, vault helper, exchange-service, withdrawal pipeline) reach the partner 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, not_found), POST /api/vault/{split,merge,convert}-signature upstream rejects from exchange-service. Branch on error.code directly — the upstream code is now lifted to the top level, so the /errors/codes catalogue is canonical regardless of whether the rejection originated in core-api or in a downstream service.
error.details.upstream still carries the original wrapper ({status, body}) for diagnostic and logging tooling that needs the upstream-side context. New partner code should rely on top-level error.code exclusively. The exchange_upstream_error wrapper code is still emitted as a fallback for non-structured upstream responses (HTML proxy page, empty body, raw text).

Why two shapes

POST /api/orders/place is the only endpoint where the matcher’s “sync” rejection path returns a structured fill record (zero-fills, zero-trades) — that record is the same shape a successful fill returns, so the schema unifies success and reject and the HTTP code stays 201. Vault signature endpoints are pure pre-flight checks with no fill record to return; they reuse the platform-wide HTTP error envelope. Look at code either way to distinguish success from reject — it’s present in both shapes.

Error codes

Complete code reference across all endpoints.