Errors
The envelope
Section titled “The envelope”Every response — success or failure — is wrapped the same way:
// success{ "state": "SUCCESS", "payload": { "...": "..." } }
// created (201 responses){ "state": "CREATED", "payload": { "...": "..." } }
// error{ "state": "SOME_ERROR_CODE", "payload": null, "details": { "...": "optional context" } }state is always present. On an error, it carries a stable, machine-readable code — parse and
branch on state, not on the HTTP status code alone, and never on the human-readable text some
errors surface in details.
HTTP status ↔ error shape
Section titled “HTTP status ↔ error shape”| HTTP status | Meaning |
|---|---|
| 200 / 201 | Success (state: "SUCCESS" / "CREATED"). |
| 400 | Request validation failed (a malformed/missing field). |
| 401 | Missing, invalid, or expired credentials/token. |
| 403 | Authenticated, but not allowed to do this specific thing (e.g. an invalid delegation target). |
| 404 | The resource doesn’t exist — or belongs to another partner. Cross-tenant access always reports 404, never 403, so a not-yours resource is indistinguishable from a genuinely nonexistent one. |
| 409 | A conflict — usually a duplicate/in-progress/racing request. |
| 422 | The request was well-formed but violates a business rule (e.g. a margin floor). |
| 429 | Rate-limited — see Rate limits. |
| 5xx | Something went wrong on our side. Retries with backoff are safe for these (and, for order creation, always safe regardless of status — see Idempotency). |
Common error codes
Section titled “Common error codes”This isn’t every code the API can ever return, but it covers what you’re likely to build error handling around:
| Code | HTTP | When |
|---|---|---|
UNAUTHORIZED_INVALID_CREDENTIALS |
401 | Wrong clientId/clientSecret at the token endpoint. |
UNAUTHORIZED_PARTNER_INACTIVE |
401 | Your partner account is inactive. |
FORBIDDEN_NOT_YOUR_DESCENDANT / FORBIDDEN_DELEGATION_TARGET_INVALID |
403 | An onBehalfOf delegation target you don’t actually have authority over. |
NOT_FOUND_ORDER / NOT_FOUND_TOPUP_ORDER / NOT_FOUND_ESIM_ORDER / NOT_FOUND_SCENT_ORDER |
404 | Unknown order id/reference, or one belonging to another partner. |
NOT_FOUND_WALLET / NOT_FOUND_STATEMENT |
404 | No wallet provisioned yet / unknown or not-yours statement. |
NOT_FOUND_WEBHOOK_SUBSCRIPTION |
404 | Unknown subscription id. |
NOT_FOUND_SCENT / NOT_FOUND_BRAND / NOT_FOUND_VARIANT |
404 | Unknown catalog entity. |
NOT_FOUND_CART_ITEM |
404 | No such line in your cart. |
NOT_FOUND_FULFILLMENT_GROUP / NOT_FOUND_ORDER_LINE |
404 | Unknown target for a return request. |
VALIDATION_MISSING_REPORT_RANGE |
400 | mode=report on /api/v1/statements without both from and to. |
VALIDATION_PRICE_LOCK_INVALID / VALIDATION_PRICE_LOCK_EXPIRED |
400 | A lockId that’s unusable — see Cart preview & price locks. |
VALIDATION_CUSTOMER_REF_TOO_LONG |
400 | customerRef over 128 characters. |
VALIDATION_WEBHOOK_EVENT_TYPE_NOT_SUBSCRIBED |
400 | Test-firing an event type your subscription isn’t subscribed to. |
VALIDATION_INVALID_REASON_CODE |
400 | An unrecognized reasonCode on a return request line. |
BUSINESS_MARGIN_BELOW_FLOOR / BUSINESS_PRICE_BELOW_COST |
422 | Live prices/costs moved enough since pricing that the order can’t be accepted at a viable margin. |
BUSINESS_WEBHOOK_SUBSCRIPTION_LIMIT |
422 | You already have 5 active subscriptions. |
BUSINESS_RETURN_REQUEST_GROUP_NOT_DELIVERED / _WINDOW_EXPIRED / _EXCEEDS_REMAINING / _DUPLICATE_LINE |
422 | A return request eligibility rule failed — see the API Reference for each. |
CONFLICT_CART_HAS_PENDING_ORDER |
409 | The cart already has an order in flight. |
CONFLICT_CART_CHECKOUT_RACE / CONFLICT_PRICE_LOCK_CREATE_RACE |
409 | A concurrent request on the same cart lost a race — safe to retry. |
RATE_LIMITED |
429 | See Rate limits. |
What error responses never do
Section titled “What error responses never do”- Never distinguish “wrong client ID” from “wrong secret” at the token endpoint — both are the
same generic
UNAUTHORIZED_INVALID_CREDENTIALS, to avoid letting a caller enumerate valid client IDs. - Never leak a stack trace, an internal exception message, or implementation detail in
details. - Never return a 200 with an error state embedded in the payload — if
stateisn’tSUCCESS/CREATED, the HTTP status code is always a matching 4xx/5xx too.