Skip to main content
Errors come back as RFC 9457 problem details with a Content-Type of application/problem+json.
Branch on status and code, and on errorKey where a transaction carries one. Never parse detail.

Authentication and access codes

These separate three problems that look identical from the outside: a signing bug, a missing permission, and a feature you have not been provisioned. Each needs a different response from you, and only the first is a bug in your integration. See Authentication for what to change in each case.

Status codes

A 401 on a call that used to work most often means a service-account credential was rotated or an environment was crossed — sandbox credentials do not work against production, and vice versa.
Insufficient balance is not one status across the API. A payout answers 402; an FX preview answers 400 with a full quote and a failureReason of NoBalance rather than a problem document — see FX conversion. Branch per endpoint rather than assuming a status means the same thing everywhere.

Why a payment failed

A failed transaction carries an errorKey on GET /v1/transactions/{reference}. This is the field to surface to your own support team.
RecipientBlocked is not a technical error. Retrying it, or routing around it with different details, may constitute circumventing a sanctions or AML control. Stop and escalate.

Retrying safely

Reads

GET requests are safe to retry. Back off exponentially on 429 and 5xx.

Writes

Send an idempotencyKey where the endpoint accepts one. Without it, a retry after an ambiguous failure risks creating a duplicate.
When a write fails and you cannot tell whether it landed, query Transactions before retrying rather than assuming either outcome.