guides
Errors
Every error uses one envelope, with normalized codes shared across all PSPs:
{ "error": { "code": "no_eligible_psp", "message": "…" } }
HTTP status mapping
| status | meaning | typical codes |
|---|---|---|
400 | invalid request | invalid_request, unsupported_payment_method, unsupported_currency |
401 / 403 | authentication | unauthorized, forbidden |
404 | unknown resource | not_found |
409 | conflict | idempotency_conflict, invalid_state_transition |
422 | routing | no_eligible_psp |
502 | PSP technical failure | psp_error, psp_timeout, psp_unavailable |
Declines are not HTTP errors
A refused card is a business outcome, not a request failure: the payment returns 201 with status: "failed" and a normalized code in last_error.code.
| decline code | meaning |
|---|---|
insufficient_funds | not enough balance |
card_declined | generic refusal |
expired_card | card expiry passed |
incorrect_cvc | CVC check failed |
do_not_honor | issuer refused without detail |
authentication_required | SCA / 3DS required |
fraud_suspected | blocked by risk checks |
The exact PSP-side reason stays visible on the attempt in the dashboard timeline — the normalized code is what your integration should branch on.