Reference
Error Codes
Every error response has the same shape — an HTTP status code, plus a stable machine-readable error.code you can branch your own logic on without parsing the human-readable message.
Error shape
json
{
"success": false,
"error": {
"code": "validation_error",
"message": "Invalid request",
"details": [
{ "loc": ["body", "amount"], "msg": "Input should be greater than 0", "type": "greater_than" }
]
}
}details is null for most error codes — it's only populated for validation_error, where it's the field-by-field list of what failed.
Codes
| HTTP Status | error.code | Meaning |
|---|---|---|
| 400 | bad_request | A generic request problem that doesn't fit a more specific code. |
| 401 | unauthorized | Missing or invalid credentials — no X-API-Key/Bearer token, or the key was revoked. |
| 403 | forbidden | Your credentials are valid, but not authorized for this merchant or resource. |
| 404 | not_found | The resource doesn't exist, or doesn't belong to your merchant. |
| 409 | conflict | The request conflicts with the resource's current state (e.g. cancelling an already-paid link). |
| 409 | idempotency_key_reused | The same Idempotency-Key was sent with a different request body. |
| 409 | insufficient_balance | A disbursement amount exceeds your current available balance. |
| 422 | validation_error | The request body failed validation — see error.details for the field-level breakdown. |
| 500 | internal_error | Something went wrong on Infinity Africa's side. Safe to retry; contact us if it persists. |
Handling errors safely
- Branch on
error.code, never onerror.message— the message text may change; the code won't. - A
409 idempotency_key_reusedalmost always means a bug in your retry logic (reusing a key across two different requests) — it's not something to retry. 500 internal_erroris safe to retry with the same idempotency key; everything else reflects something about the request itself that a retry won't fix.
