> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orqex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> HTTP failures, and why a declined payment is not one of them.

## A failed payment is not an HTTP error

This is the distinction that matters most. If a customer's payment is declined, the request
still succeeds: you get a `2xx`, and the failure is described inside the resource, on the
attempt's `failure` block.

An HTTP error means Orchestrate could not process your **request** — bad credentials, invalid
input, a resource that does not exist. Handle the two separately.

```json theme={null}
{
  "failure": {
    "code": {
      "value": "...",
      "category": "CUSTOMER_ERROR",
      "message": "..."
    },
    "message": "..."
  }
}
```

## Failure categories

Every payment failure carries a category. Branch on the category, not on the individual
code: codes are added over time, categories are stable.

| Category                | What it means                                                          | What to do                                        |
| ----------------------- | ---------------------------------------------------------------------- | ------------------------------------------------- |
| `CUSTOMER_ERROR`        | The customer can fix it — wrong code, insufficient balance, cancelled. | Show the message and let them try again.          |
| `GATEWAY_ERROR`         | The provider failed or was unavailable.                                | Transient. Retrying later is reasonable.          |
| `TELCO_ERROR`           | The mobile network operator failed.                                    | Transient. Retrying later is reasonable.          |
| `MERCHANT_CONFIG_ERROR` | Your project configuration cannot serve this payment.                  | Fix it in your dashboard; retrying will not help. |
| `PLATFORM_ERROR`        | Something went wrong on the Orchestrate side.                          | Contact support with the payment id.              |
| `SECURITY_ERROR`        | The payment was blocked by an integrity or fraud control.              | Do not retry programmatically.                    |

## HTTP status codes

| Status | Meaning                                                                                                                                 |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| `200`  | Success.                                                                                                                                |
| `201`  | Resource created.                                                                                                                       |
| `401`  | No key, or the key is invalid, inactive or expired.                                                                                     |
| `403`  | The request address is not on the key's IP allowlist.                                                                                   |
| `404`  | The resource does not exist, or is not visible to this key's environment.                                                               |
| `409`  | Conflict — most often a payout `reference` you have already used.                                                                       |
| `422`  | The request was well-formed but rejected: validation failed, the intent is already final, or the service declined the refund or payout. |
| `429`  | Rate limit exceeded. Back off and retry.                                                                                                |
| `5xx`  | Server error. Retry with backoff; use an idempotency key so the retry is safe.                                                          |

## Error shape

Errors carry a human-readable message. Validation errors add a field-by-field breakdown.

```json theme={null}
{
  "message": "The given data was invalid.",
  "errors": {
    "currency": ["The currency field must be 3 characters."]
  }
}
```

Show `message` to your operators, not to your customers — for customers, use the
`failure.code.message` on the attempt, which is written for them.

## Retrying safely

`429` and `5xx` are worth retrying with exponential backoff. Always send an
[idempotency key](/api-conventions#idempotency) on the original request so the retry cannot
create a second payment.

`4xx` other than `429` will not succeed on retry. Fix the request first.
