Error body
Every error is a flat JSON object. The minimum, always-present shape:Branch on the slug, not the message
Two internal layers can produce an error, and they populate the slug field differently. A business-rule error from a/v1 endpoint (a bad amount, a missing scope, a firm that is not connected) sets error to the constant "request_error" and puts the discriminating slug in code. A transport or framework error (an oversized body, an unmatched route, an internal fault) puts the slug directly in error and has no code.
One line reads the right slug in both cases:
message. It is generic and its wording can change without notice.
The slug vocabulary
Every slug maps to exactly one HTTP status. Handle the status class; treat the slug set as open and default anything you do not recognise.Two slugs appear for 422 and 500 because either layer can raise them: the trading surface emits
unprocessable / internal, the transport layer emits validation_error / internal_error. Reading code ?? error and switching on the shared HTTP status keeps a client correct regardless of which layer answered.The request id is on every response
Every response, success or error, carries anX-Request-ID header: the id inbound from your gateway if you sent one, otherwise a fresh one ScripX generates. It stamps the matching server-side log line, so one id maps a client-observed error to its server trace.
request_id. Read it from the header when you want it unconditionally, log it on every non-2xx, and quote it in any support request. It identifies exactly one request and contains nothing sensitive.
Body-size limit
A request body over 1 MiB is rejected up front, before any parsing, with413 request_too_large:
POST /v1/orders:batch: keep a batch within the 500-order cap and its body stays well under the limit.
Rate-limit errors
A429 carries Retry-After: 60 plus the full X-RateLimit-* header set, so a correct client never guesses. The header table and back-off guidance are in Rate limits. The public surface is metered separately per IP and returns a retry_after_s hint in its detail.
Partial success on batches
POST /v1/orders:batch does not fail wholesale: valid orders submit, invalid ones return per-item {index, ok, status, error} results in the same 200 response. Inspect each result rather than the top-level status.
Handling errors well
- Read
code ?? errorfor the stable slug; switch on it and the HTTP status, never onmessage. - Log
X-Request-IDon every non-2xx, and quote it when you contact support. - Retry only
429(afterRetry-After) and500(with backoff). Treat4xxother than429as a bug to fix, not to retry. - Make retries safe: send an
Idempotency-Keyon every order so a retried write can never double-place.
Related
- Authentication, the 401/403/429 auth table.
- Rate limits, 429, quotas and the header set.
- Idempotency, safe retries on writes.
- Webhooks, delivery failures retry and dead-letter rather than error.