01API
API v1For developers
Errors
Every error has a status and a body. The code never changes within version 1; the message is for people and may be reworded.
JSON
| Field | Meaning |
|---|---|
code | A stable word. Branch on this. |
message | A plain sentence. Show it to a person; do not parse it. |
request_id | Also in the X-Request-Id header of every response. Quote it to support and the call can be found. |
fields | For bad_request about a body: [{ "field": "lines[0].quantity", "message": "must be a whole number" }] |
required | Only with missing_permission: the permission the call needed. |
lock | Only with line_locked: why (shelf, ready, failed, placed). |
The three catalog routes
GET /products, GET /products/{key} and GET /staff/products/{key} were the first routes of the API and keep their original bodies, so existing clients keep working: error is a bare word (unauthorized, forbidden, not_found, rate_limited, bad_request, setting_up, unavailable), and they add error_detail: { code, message } with the same codes as below. Their request id is in the X-Request-Id header only. A client that handles both reads error.code ?? error_detail.code ?? error.
Codes
| Status | code | Meaning |
|---|---|---|
| 400 | bad_request | A parameter or body is wrong; fields says which |
| 400 | bad_cursor | The cursor is malformed or belongs to another request. Start again without it. |
| 400 | card_data_rejected | A body field is named like card data. Payment stays with the store; remove it. |
| 400 | idempotency_required | This call needs an Idempotency-Key |
| 401 | missing_key | No Authorization: Bearer header |
| 401 | bad_key | Unknown or revoked key, or a suspended store |
| 403 | missing_permission | The key lacks a permission; required names it |
| 403 | wrong_scope | A key of the wrong kind for the route |
| 403 | server_side_only | A browser's request on a server-to-server route |
| 403 | plan_not_allowed | The store's plan does not include this |
| 403 | billing_read_only | A write while the store's account is read-only |
| 404 | not_found | No such thing in this store |
| 409 | idempotency_conflict | The Idempotency-Key was used for a different request |
| 409 | intake_conflict | An order with this intake_ref was sent with different contents |
| 409 | po_conflict | The dealer_po is used by another order |
| 409 | already_matched | The order is already matched to a different store order |
| 409 | store_order_number_in_use | Another order has this store order number |
| 409 | already_shipped | Shipped already, with different tracking |
| 409 | order_cancelled, order_closed | The order is cancelled or closed, so this cannot be done |
| 409 | line_locked | The line is on a distributor order that was sent, is ready or failed, or is filled from the shelf; lock and the message say why |
| 409 | not_a_transfer | Ready, ship and deliver apply to an order that ships to an FFL |
| 409 | not_a_draft | Only a distributor-order draft can change |
| 409 | fastbound_managed | The item comes from FastBound; change it in FastBound |
| 409 | invalid_state | The thing is in a state that does not allow this; the message says how |
| 413 | too_large | The body or batch is over the limit |
| 422 | ffl_not_allowed | The chosen FFL cannot be used (one answer for unknown, hidden, expired and blocked) |
| 429 | rate_limited | Over the limit; wait Retry-After seconds |
| 503 | setting_up | The store's database update is not finished. Try again later. |
| 503 | unavailable | A fault on Rollmark's side. Retry with the same Idempotency-Key. |
What to do
- 4xx: do not send the same request again; fix it. (
409s that say a state,429and503are the exceptions: read the code.) - 429 and 5xx: wait (
Retry-Afterif given, else back off), then send it again with the sameIdempotency-Key. It is safe. - Log
request_idwith every failure.