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

# Error Handling

> Understanding and handling API errors

All Ratio API errors follow a consistent JSON format with machine-readable codes for programmatic handling and human-readable messages for debugging.

## Error response format

```json theme={null}
{
  "error": {
    "code": "QUOTE_EXPIRED",
    "message": "Quote has expired; please re-request",
    "corridor": "USD-IDR",
    "timestamp": "2026-02-27T10:05:00Z",
    "request_id": "req_abc123"
  }
}
```

<ResponseField name="error.code" type="string">
  Machine-readable error code. Use this field for programmatic handling — do not parse `message`.
</ResponseField>

<ResponseField name="error.message" type="string">
  Human-readable description of the error.
</ResponseField>

<ResponseField name="error.corridor" type="string">
  Affected corridor, if applicable.
</ResponseField>

<ResponseField name="error.timestamp" type="string">
  ISO 8601 server timestamp.
</ResponseField>

<ResponseField name="error.request_id" type="string">
  Unique request identifier. Include this when contacting support.
</ResponseField>

## FX Engine errors

These errors originate from the FX Engine and are specific to quote and execution operations.

| HTTP  | Code                         | Description                                         | Resolution                                                      |
| ----- | ---------------------------- | --------------------------------------------------- | --------------------------------------------------------------- |
| `400` | `BELOW_MIN_TRANSACTION_SIZE` | Amount is below the minimum for this corridor       | Increase amount to meet the corridor's minimum transaction size |
| `400` | `DIRECTION_BLOCKED`          | Trade direction not permitted under current state   | Wait for state recovery or try the opposite direction           |
| `400` | `ARB_CHECK_FAILED`           | Quote failed internal arbitrage validation          | Re-request — market conditions may have shifted                 |
| `400` | `QUOTE_EXPIRED`              | Firm quote has expired                              | Request a new firm quote at current rates                       |
| `422` | `NO_VIABLE_RFQ_QUOTE`        | No acceptable external quote available              | Retry with a smaller size or wait for liquidity to improve      |
| `422` | `CROSS_LEG_INSUFFICIENT`     | Insufficient liquidity for cross-corridor execution | Retry with a smaller size                                       |
| `503` | `ORACLE_STALE`               | Oracle price data is outdated                       | Wait for oracle recovery (typically seconds)                    |
| `503` | `ORACLE_DEVIATION`           | Oracle sources diverge beyond threshold             | Wait for oracle convergence                                     |
| `503` | `QUOTE_HALTED`               | System is in HALT state — no new quotes             | Wait for system recovery; monitor `GET /v1/system/state`        |

## General API errors

| HTTP  | Code             | Description                              | Resolution                                                      |
| ----- | ---------------- | ---------------------------------------- | --------------------------------------------------------------- |
| `401` | `UNAUTHORIZED`   | Missing or invalid Bearer token          | Check your API key                                              |
| `403` | `FORBIDDEN`      | Valid token but insufficient permissions | Request the appropriate access scope from your account manager  |
| `404` | `NOT_FOUND`      | Resource does not exist                  | Verify the corridor name, quote ID, or endpoint path            |
| `429` | `RATE_LIMITED`   | Too many requests                        | Implement exponential backoff; respect the `Retry-After` header |
| `500` | `INTERNAL_ERROR` | Unexpected server error                  | Retry after a short delay; contact support if errors persist    |

## Retry guidance

| HTTP range | Category                     | Recommended action                                                                     |
| ---------- | ---------------------------- | -------------------------------------------------------------------------------------- |
| **400**    | Client error or timing issue | Fix the request parameters or request a fresh quote. Do not retry the same request.    |
| **422**    | Liquidity or size constraint | Reduce trade size, try a different corridor, or wait for liquidity to improve.         |
| **429**    | Rate limit                   | Wait for the duration specified in the `Retry-After` header, then retry.               |
| **500**    | Server error                 | Retry with exponential backoff (1s → 2s → 4s → 8s). Contact support if errors persist. |
| **503**    | System-level issue           | Wait 5–30 seconds and retry. Check `GET /v1/system/state` for corridor availability.   |

<Warning>
  Never retry `400` errors with the same parameters. They indicate a client-side issue that must be corrected before retrying.
</Warning>

## Best practices

* **Always check `error.code`** for programmatic handling. Do not parse the `message` string — it is intended for human debugging only and may change.
* **Log `error.request_id`** for every error response. This is essential for support escalation.
* **Monitor `GET /v1/system/state`** proactively rather than waiting for `503` errors. This lets you disable corridors in your UI before users encounter failures.
* **Implement graceful degradation.** When a corridor enters a protective state, show users a clear message rather than exposing raw error codes.
* **Use exponential backoff** for `500` and `503` retries to avoid overwhelming the system during recovery.
