---
title: Errors
description: The error shape and every code you can get back.
group: concepts
order: 6
---

Every response has the same four fields, errors included:

```json
{
  "success": false,
  "data": null,
  "message": "Insufficient wallet balance",
  "code": "WALLET_INSUFFICIENT_FUNDS"
}
```

On success `code` is always `OK`. Branch on `code`, not on `message`: messages may be reworded, codes never change.

A validation error names each field in `data.errors`:

```json
{
  "success": false,
  "data": { "errors": [{ "field": "amount", "message": "must be an amount in naira with at most 2 decimal places" }] },
  "message": "must be an amount in naira with at most 2 decimal places",
  "code": "VALIDATION_FAILED"
}
```

## Codes

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `VALIDATION_FAILED` | A field is missing or malformed, or the `product_code` cannot be sold right now; `data.errors` names the field |
| 400 | `IDEMPOTENCY_KEY_MISSING` | A sale without an `Idempotency-Key` header |
| 401 | `AUTH_REQUIRED` | No key, a key that does not exist, or a missing or invalid request signature |
| 402 | `WALLET_INSUFFICIENT_FUNDS` | The available balance cannot cover the sale's fee |
| 403 | `PERMISSION_DENIED` | The key has an IP allowlist and this address is not on it, or the key may not do this |
| 404 | `NOT_FOUND` | No such transaction or resource |
| 409 | `IDEMPOTENCY_IN_PROGRESS` | The same idempotency key is still running |
| 409 | `CONFLICT` | The `client_reference` is already used; `message` names that sale's reference |
| 413 | `REQUEST_TOO_LARGE` | The body is too large |
| 422 | `IDEMPOTENCY_FINGERPRINT_MISMATCH` | The same idempotency key with a different body |
| 429 | `RATE_LIMITED` | Too many requests; wait for `Retry-After` |
| 500 | `INTERNAL_ERROR` | Something broke on our side; safe to repeat with the same `Idempotency-Key` |
| 503 | `SERVICE_UNAVAILABLE` | We are briefly unavailable; repeat with the same `Idempotency-Key` |

## Failed sales

A failed sale is not an error: it answers `200` with `status` `failed`, a `failure_code` and a `failure_message`, and its hold is released.

| Failure code | Meaning |
| --- | --- |
| `ROUTING_NO_CANDIDATES` | No line or channel could take the sale right now |
| `PROVIDER_REJECTED` | The sale was refused for good, such as an invalid number; it is not tried again |
| `RETRIES_EXHAUSTED` | Every line or channel tried failed for a passing reason |
| `PROVIDER_FAILED` | The line or channel reported the sale failed |
