Core conceptsErrors
Errors
Branch your logic on error.code, never on error.message — messages are
human-readable and may change. Always log requestId; it is what support needs to trace a call.
HTTP status meanings
| Status | Meaning | Retry? |
|---|---|---|
| 400 | Malformed request, failed validation, or missing required header | No — fix the request |
| 401 | Credentials or signature missing, invalid or expired | No — fix authentication |
| 403 | Authenticated, but lacks access or the required scope | No |
| 404 | Does not exist, or is not visible to this seller | No |
| 409 | Conflicts with current state, or an idempotency key is in flight | Yes, after Retry-After |
| 413 | Body exceeds 1 MB | No — split the payload |
| 422 | Well-formed but not processable in the resource's current state | No |
| 429 | Rate limit exceeded | Yes, with backoff |
| 500 | Unexpected platform error | Yes, with backoff. Report the requestId |
| 502 / 504 | Upstream commerce service failed or timed out | Yes, with backoff |
| 503 | A platform dependency is temporarily unavailable | Yes, after Retry-After |
Commonly encountered codes
| Code | HTTP | When you'll see it | Resolution |
|---|---|---|---|
| VALIDATION_ERROR | 400 | Body or query failed schema validation | Read error.message; check for unknown fields — schemas are strict |
| INVALID_JSON | 400 | Body is not parseable JSON | Check serialisation and Content-Type |
| CATEGORY_NOT_FOUND | 400 | The category you sent is not a known category ID | Send an ID from GET /categories, not the name returned by product reads |
| NOT_FOUND | 404 | Missing, or belongs to another seller | Verify the seller token matches the resource's owner |
| CONCURRENT_MODIFICATION | 409 | The resource changed between your read and write | Re-read and reapply your change |
| INVALID_TRANSITION | 422 | The state change is not legal from the current state | Re-read the resource; another actor may have advanced it |
| ORDER_NOT_CANCELLABLE | 422 | The order has progressed past a cancellable state | Handle as a return instead |
| SHIPMENT_NOT_ARRANGED | 422 | Tracking requested before a shipment exists | Arrange a shipment first via POST /orders/{id}/shipment |
| SHIPPING_LABEL_UNAVAILABLE | 422 | Label requested before a shipment exists | As above |
| SHIPMENT_ALREADY_ARRANGED | 422 | The order already has a booking | Read the existing booking via GET /orders/{id}/tracking |
| INVALID_PICKUP_DATE | 422 | Date rejected by cutoff, holiday or ship-by rules | Pick from GET /orders/{id}/shipment/eligible-dates |
| SANDBOX_ONLY | 403 | A sandbox route was called with live credentials | Use sandbox credentials and the sandbox host |
| VALIDATION_ERROR | 400 | A webhook callback URL resolved to a private, loopback or metadata address (SSRF safety check) | Use a publicly resolvable HTTPS URL — see Webhooks |
| UPSTREAM_ERROR | 502 / 503 | The commerce service was briefly unavailable for a proxied shipment call | Retry with backoff — this is transient, not a blocked operation |
The complete machine-readable catalog — every code the platform can emit, with HTTP status and resolution — is the
ErrorResponse.error.code enum in the OpenAPI spec and the interactive reference. Both are generated from
the same source, so a code you receive at runtime is always present in both.