Errors
Conventional HTTP status codes, and one envelope for every failure — so you can write a single error handler and be done.
{
"error": {
"code": 400,
"message": "Validation failed.",
"details": {
"items": {
"0": {
"quantity": ["Must be greater than zero."]
}
}
}
}
}Status codes
400Bad request
Validation failed. `details` names the offending fields, nested to match your payload.
Fix the payload. Retrying unchanged will fail again.
401Unauthenticated
The access token is missing, malformed, or expired.
Refresh the token and retry once.
403Forbidden
You are authenticated but not allowed: your role lacks the permission, or X-Shop-Id names a shop this credential cannot reach.
Do not retry. Fix the permission or the shop id.
404Not found
No such record *in this shop*. A valid id from another shop is a 404 here, not a 403 — we do not confirm that other shops' records exist.
Do not retry.
409Conflict
The operation is valid but the world disagrees — most often insufficient stock.
Re-read the current state before retrying.
429Rate limited
1,000 requests per hour per shop. A Retry-After header tells you how many seconds to wait.
Back off for Retry-After seconds, then retry.
500 / 503Server error
Something broke on our side, or a dependency is unavailable.
Retry with exponential backoff. If the write was idempotent, retrying is safe.
Insufficient stock
The one you will hit in the wild. A sale is rejected in full — we never sell you part of a basket — and nothing is written: no stock moved, no payment taken, no ledger entry.
{
"error": {
"code": 409,
"message": "Need 5 but only 2 available for NES-200 at Main Store.",
"details": {
"variant": "NES-200",
"requested": "5.000",
"available": "2.000"
}
}
}