---
title: Errors
description: The response envelope, HTTP status codes, and what a failed transaction's reason means.
---

## Response envelope

Every response, success or failure, has the same shape:

```json
{
  "success": true,
  "message": "payment initiated",
  "data": { }
}
```

- **`success`** · `boolean` · required

  Whether the request succeeded. Check this before reading `data`.

- **`message`** · `string` · required

  What happened. On failure, says what went wrong in words you can show a user.

- **`data`** · `object \| array \| null` · required

  The endpoint's result. `null` on failure.

## Status codes

| Code | Meaning |
| --- | --- |
| `400` | The request can't be done as sent: a missing field, an invalid phone number, a limit exceeded, a provider that isn't enabled. `message` says which. |
| `401` | Missing, unknown or revoked API key. |
| `403` | The key lacks the [permission](/docs/concepts/authentication#permissions) this endpoint needs. |
| `404` | Nothing with that reference in your project. |
| `429` | Too many requests. See [Rate limits](/docs/concepts/rate-limits). |
| `500` | Something went wrong on our side. Safe to retry with the same `external_reference`. |

```json
{
  "success": false,
  "message": "provider \"momo\" is not enabled for this project",
  "data": null
}
```

A `400` from a write means nothing was created, with one exception: if the provider rejects the request after RazPay recorded it, the transaction exists as `failed` and the message starts `failed to initiate payment:`, `failed to send payout:` or `failed to send refund:`, followed by the reason.

## Failure reasons

A failed transaction's `failure_reason` is a plain-language explanation built from the provider's error code. These are the common ones from MTN MoMo:

| Reason | What to do |
| --- | --- |
| `payer account could not be found` | The number isn't a MoMo wallet. Check it with the customer. |
| `payee account could not be found` | The payout number isn't a MoMo wallet. |
| `payment was rejected by the payer` | The customer declined the prompt. |
| `payment request expired before it was approved` | The customer didn't approve in time. |
| `payment timed out before it was approved — approve within 5 minutes` | Same: ask the customer to try again and approve quickly. |
| `insufficient funds` | The paying wallet (or your disbursement account) lacks the balance. |
| `payer has reached their transaction limit` | The customer has hit their wallet limit. |
| `this payment is not allowed` | MTN refused the operation for this account; contact your MTN account manager. |
| `currency is not supported for this account` | Use the currency of your MTN environment (`SZL` in Eswatini production). |
| `payment provider is temporarily unavailable, please try again shortly` | Retry later. |

MTN sometimes returns codes it doesn't document. Those come through as the code in plain words (`SOME_NEW_CODE` becomes `some new code`), so you always learn something concrete.
