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

# Error Reference

> Every error the API returns and how to resolve it

Errors are returned with an HTTP status code and a message:

```json theme={null}
{
  "status": 400,
  "message": "Decryption failed"
}
```

## Authentication & access errors

| HTTP | Message                                           | Cause                                               | Fix                                               |
| ---- | ------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------- |
| 400  | `No token provided`                               | Missing `Authorization` header                      | Send `Authorization: Bearer <api key>`            |
| 400  | `Invalid token prefix`                            | Key doesn't start with `cngn_test`/`cngn_live`      | Check you copied the full key                     |
| 400  | `Merchant not found`                              | API key not recognised                              | Regenerate the key in the dashboard               |
| 400  | `No Test SSH Key found` / `No Live SSH Key found` | No Ed25519 public key uploaded for this environment | Upload your public key in the dashboard           |
| 403  | `IP address not whitelisted`                      | Request came from a non-whitelisted IP              | Add your server IP in dashboard security settings |
| 403  | `Could not determine client IP address`           | Source IP could not be resolved                     | Check proxy configuration (`X-Forwarded-For`)     |
| 403  | `Permission denied`                               | Your role lacks the required permission             | See [Permissions](/guides/permissions)            |
| 404  | `Merchant not found`                              | Business role could not be resolved                 | Contact support                                   |

## Encryption errors

| HTTP | Message                               | Cause                                              | Fix                                                    |
| ---- | ------------------------------------- | -------------------------------------------------- | ------------------------------------------------------ |
| 400  | `Missing encryption data, key, or IV` | Body sent as plain JSON instead of `{content, iv}` | Encrypt the body; see [Encryption](/guides/encryption) |
| 400  | `Decryption failed`                   | Wrong AES key, malformed base64, or corrupted IV   | Verify the encryption key and IV generation            |

## Validation errors

Request bodies are validated after decryption. Validation failures return `400` with a `field: message` string, for example:

```json theme={null}
{
  "status": 400,
  "message": "amount: Number must be greater than or equal to 1"
}
```

## Rate limiting

| HTTP | Message                                      | Fix                                                       |
| ---- | -------------------------------------------- | --------------------------------------------------------- |
| 429  | `Too many requests. Please try again later.` | Wait 60 seconds; see [Rate Limiting](/guides/rate-limits) |

## Service availability

| HTTP | Message                                                     | Cause                                                           |
| ---- | ----------------------------------------------------------- | --------------------------------------------------------------- |
| 400  | `Service is currently unavailable. Please try again later.` | Redeem or withdrawal service temporarily disabled platform-wide |

## Business-logic errors

Endpoint-specific failures (insufficient balance, invalid bank account, unsupported network, etc.) also return `400` with a descriptive message. Treat any unrecognised `400` message as non-retryable without changing the request.

<Tip>
  **Retry guidance:** retry `429` (after the block window) and `5xx` with exponential backoff. Do not blindly retry `400` responses on money-moving endpoints. Verify state first with [Verify Withdrawal](/api-reference/verify-withdrawal) or [Get Transactions](/api-reference/get-transactions) to avoid duplicate transfers.
</Tip>
