---
title: Errors
description: The error format and the codes you may see.
---

Failed requests return a non-`2xx` status and this body:

```json
{
  "error": {
    "code": "invalid_input",
    "message": "amount must be a decimal string such as \"120.00\""
  }
}
```

Branch on `code`. The `message` is for people and can change. Server errors (`5xx`) carry a generic message.

Every response, success or failure, has a `Request-Id` header. Log it. Give it to support if you need help.

## Codes

| Status | Code                          | Meaning and what to do                                                            |
| ------ | ----------------------------- | --------------------------------------------------------------------------------- |
| 400    | `invalid_input`               | A field is missing or malformed, the `Idempotency-Key` is missing or too long, or the fee cannot be priced. Read `message`, fix the request. |
| 401    | `unauthenticated`             | The token is missing, invalid or expired. Get a new token.                        |
| 403    | `forbidden`                   | The token belongs to a signed-in user, not an API key. Or the key lacks the permission this route needs. Use an API key's token, or add the permission to the key. |
| 403    | `environment_mismatch`        | The key belongs to the other environment. Use a key from this environment.        |
| 403    | `tier_ineligible`             | Your verification level does not allow this payment. See [Going live](/guides/going-live). |
| 403    | `transaction_limit_exceeded`  | The amount is over the limit for one payment.                                     |
| 403    | `daily_limit_exceeded`        | You reached today's limit. Check `GET /v1/limits`.                                |
| 403    | `monthly_limit_exceeded`      | You reached this month's limit. Check `GET /v1/limits`.                           |
| 403    | `limits_missing`, `limits_unusable` | Your limits could not be read or used. Contact support with the `Request-Id`. |
| 403    | `business_suspended`          | iSmartPay has suspended your business. API keys stop working and no payment can be made until it is reinstated. The account owner was told why by email and SMS. Contact support. |
| 403    | `business_deactivated`        | Your business is deactivated. No payment can be made. Contact support. |
| 404    | `not_found`                   | Nothing was found with that reference or number in this environment.              |
| 409    | `idempotency_key_reused`      | This key was used with a different body. Use a new key, or send the original body. |
| 409    | `request_in_flight`           | The first request with this key is still running. Wait, then retry the same request. |
| 409    | `duplicate_reference`         | This `reference` already belongs to a different payment. Use a new reference. |
| 422    | `destination_not_verified`    | The provider could not confirm a settlement destination. Nothing changed. `message` names the destination. |
| 429    | `rate_limited`                | Too many requests for this key. Wait `Retry-After` seconds, then retry.           |
| 502    | `internal`                    | A service behind the API did not answer usably. Retry a `POST` with the same `Idempotency-Key` and body. |
| 502    | `provider_error`              | The mobile money or bank provider refused a lookup. Check the number or account, then try again. |
| 503    | `environment_unknown`         | The API cannot tell which environment it serves right now. Retry shortly.         |
| 503    | `destination_verification_unavailable` | The provider could not be reached to confirm a settlement destination. Nothing changed. Retry the same request shortly. |
| 504    | `upstream_timeout`            | The request took too long. A `POST` may or may not have run. Retry with the same `Idempotency-Key` and body, or read it back by `reference`. |
| 504    | `provider_timeout`            | The provider did not answer a lookup in time. Try the lookup again. |
| 500    | `internal`                    | Our fault. Retry a `POST` with the same `Idempotency-Key` and body. Give the `Request-Id` to support if it continues. |

Some `403` and `409` responses can carry a code not listed here. Branch on the status when you meet one you do not know, and log the `code`.

## Retrying

- Retry `429` and `5xx` after a delay. For `429`, wait at least `Retry-After` seconds. Back off a little more each time.
- Send the same `Idempotency-Key` and body on a retry of a `POST`.
- Do not retry `400`, `401` (without a new token), `403` or `404`. The same request will fail the same way.
