Errors
Every error response has the same JSON shape:
{ "error": "VALIDATION_ERROR", "message": "amount_ghs must be greater than zero", "request_id": "0c6f2a8e-9a1b-4d7e-8f3c-5b2a1e9d4c70", "timestamp": "2026-11-15T10:04:05Z", "retriable": false}| Field | Meaning |
|---|---|
error |
Machine-readable code in SCREAMING_SNAKE_CASE. Branch on this. |
message |
Human-readable detail. Do not parse it; it can change. |
request_id |
Unique ID for the request, also sent in the x-request-id header. Include it when you contact support. |
timestamp |
When the error happened. |
retriable |
true only when retrying the same request may succeed. |
Some errors add extra fields next to these (for example available_balance on a payout that exceeds your balance).
Status codes
Section titled “Status codes”| Status | Meaning | Retry? |
|---|---|---|
400 |
The request is invalid (bad field, unregistered callback_url). |
No. Fix the request. |
401 |
The API key is missing, invalid, expired or revoked. | No. |
403 |
The key lacks the required scope, the merchant account is not active, the payment belongs to another merchant, or the environment is not allowed (for example a live key requesting a payout). | No. |
404 |
The resource does not exist, or is in the other environment (a test key cannot see live payments). | No. |
409 |
The idempotency key was reused with different details. | No. Use the original details or a new key. |
429 |
Too many requests. | Yes, after a delay. |
500 |
Unexpected server error. | Reads: yes, with backoff. Writes: only with the same idempotency key. |
502, 503, 504 |
Temporary upstream or availability problem. | Yes, with backoff. |
Retrying safely
Section titled “Retrying safely”- Retry only when
retriableistrue, with exponential backoff and a limit. - For anything that creates something (payments, payouts), always send an idempotency key so a retry cannot create a duplicate. See Idempotency.
- The SDK retries reads automatically (up to 3 attempts on
429and5xx, honouringRetry-After). It never retries writes on its own; you decide, using your idempotency key.
The SDK turns every failure into an SDKError with status, code, message, request_id and retriable, so you can handle API errors and network errors the same way.