Skip to content

Errors

Errors use the same envelope everywhere, with a stable code you can branch on.

{
  "success": false,
  "error": {
    "code": "validation_error",
    "message": "name is required",
    "details": { "field": "name" }
  }
}
  • code — a stable machine-readable string (safe to switch on).
  • message — human-readable, may change wording over time.
  • details — optional structured context (the offending field, required scope, …).

The HTTP status code matches the error.

HTTP code Meaning
400 bad_request malformed request
401 unauthorized missing or invalid API key
403 forbidden key lacks the required scope (details.required)
403 limit_exceeded your plan's limit is reached; message names it (e.g. Limit: sales)
404 not_found the resource does not exist (or isn't yours)
405 method_not_allowed that HTTP method isn't supported on this path
409 conflict the request conflicts with current state (e.g. a sale with this external_id already exists — details.sale_id)
422 validation_error a field failed validation (details.field)
422 carrier_error the delivery service rejected the request (e.g. a waybill); message carries its text
422 promotion_* the promotion of a sale item was not applied, see Sale with a promotion
429 rate_limited over the rate limit — retry after Retry-After seconds
500 server_error an unexpected error on our side
502 carrier_error the delivery service did not answer (e.g. an address lookup) — retry later

Branch on code, not message

Treat message as display text only. Program against code and the HTTP status.

Composite /query isolates errors per sub-query — a failing sub-query carries this same error object in its slot while the others still return data. See Composite query.