Error codes
One envelope for every failure:
{ "error": { "code": "invalid_request", "message": "…" } }
| HTTP | code |
When | What to do |
|---|---|---|---|
| 401 | missing_api_key |
No Authorization: Bearer sk_… and no X-API-Key |
Add the key |
| 401 | invalid_api_key |
Key revoked, unknown, or account inactive | Check the key and the account status |
| 403 | insufficient_scope |
The key does not carry that right (paying out, say) | Issue a key with the right scope |
| 403 | client_inactive |
The account is not approved, or is suspended | Finish the KYB file |
| 403 | not_live |
Account not yet open to live mode | Use an sk_test_ key meanwhile |
| 404 | not_found |
No such resource — or not yours | Check the identifier |
| 409 | already_exists |
Duplicate (reference already used) | Read the existing resource |
| 409 | precondition_failed |
The state does not allow the operation | Read the resource, fix the sequence |
| 409 | concurrent_request |
Same Idempotency-Key, different payload |
Do not reuse one key for two operations |
| 422 | invalid_request |
Invalid body (field, amount, asset) | The message names the offending field |
| 422 | insufficient_funds |
Not enough balance for this payout | Check GET /v1/balances |
| 429 | — | 300 requests/minute exceeded | Honour the Retry-After header |
| 502 | upstream_error |
A dependency failed | Replay with the same Idempotency-Key |
| 503 | temporarily_unavailable |
Passing unavailability | Replay with backoff |
Replaying safely
502 and 503 are always replayed with the same Idempotency-Key: if the first request had
succeeded on our side, you get its result instead of creating a duplicate.
A
502does not mean nothing happened. It says a dependency did not answer, not that the operation does not exist. That is exactly the situation idempotency exists for: replay, do not recreate.
not_found on a resource that exists
The API also answers 404 when the resource belongs to another merchant: never infer a resource's
existence from a 404.
A refused body is not an ignored field
An unknown field in a JSON body is silently ignored, not rejected. A typo in an optional field
name therefore produces no error: it produces an operation created without what you believed you
had sent. Read the names in the reference rather than trusting the absence
of a 422.