Errors and retries
Errors always come back in the standard envelope with
status: CANCELLED. The machine-readable part is statusCode — an internal
application code that does not match the HTTP status.
StatusCode enum
| Code | Name | HTTP | Meaning | Retry? |
|---|---|---|---|---|
0 | OK | 200 | Success | — |
1 | ACCEPTED | 202 | Accepted for async processing | Poll |
2 | IDEMPOTENCY_REPLAY | 200 | Idempotent replay — the original result | — |
3 | VALIDATION_ERROR | 422 / 400 / 415 | Validation failed | ❌ Fix the request |
4 | UNAUTHORIZED | 401 | Not authenticated | ❌ |
5 | FORBIDDEN | 403 | No scope, IP not in allowlist, or key revoked | ❌ |
6 | NOT_FOUND | 404 | Resource not found | ❌ |
7 | CONFLICT | 409 | Conflict / duplicate | ❌ |
8 | LIMIT_REACHED | 429 | Per-window rate limit hit (ours or the upstream shop service) | ✅ Back off |
9 | OUT_OF_STOCK | 422 | Not in stock | ❌ (retry later at your discretion) |
10 | INSUFFICIENT_FUNDS | 422 | Not enough balance | ❌ Top up first |
11 | UPSTREAM_ERROR | 502 | Upstream service error | ✅ Careful retry |
12 | INTERNAL_ERROR | 500 | Server error | ✅ Careful retry |
13 | API_KEY_LIMIT_EXCEEDED | 409 | Order does not fit the key's transactionLimit | ❌ Raise the limit |
14 | PROVIDER_UNAVAILABLE | 504 | Provider did not answer after our retries. Order not created, funds not charged. Retry-After: 2 | ✅ Retry |
Retry policy that works
statusCode 8 → sleep Retry-After (or exponential back-off), retry
statusCode 11 → exponential back-off, cap at ~3 attempts
statusCode 12 → exponential back-off, cap at ~3 attempts
statusCode 14 → honour Retry-After (2s), retry
everything else → do not retry; fix the request or the key
Always reuse the same referenceId when retrying a purchase. That is what
turns a retry into a replay instead of a second charge — see
Idempotency.
Two 409s that mean opposite things
Both return HTTP 409, and only one of them is worth reacting to programmatically:
statusCode: 13+errorCode: "api_key_transaction_limit_exceeded"— the key's spend cap is exhausted. Permanent until raised. No amount of back-off helps.statusCode: 7— a plain conflict, usually a duplicate.
:::info Changed in 2026-05
Transaction-limit exhaustion moved from 429 to 409. If your client special-cased
429 for "key limit reached", move that branch to
409 + errorCode="api_key_transaction_limit_exceeded" and leave only true
rate-limit back-off on 429. See Authentication.
:::
PROVIDER_UNAVAILABLE is safe to retry
statusCode 14 is the one upstream failure with a guarantee attached: the order
was not created and nothing was charged. We already exhausted our internal
retries before returning it. Honour Retry-After: 2 and send the request again
with the same referenceId.
Reporting a problem
Include the traceId from the envelope (or the X-Trace-Id response header).
It resolves the request end to end in our logs — timestamps and endpoint names
alone usually do not.