# Errors, Rate Limits, And Idempotency

Use structured errors and idempotency keys to make your integration safe under retries, network failures, and duplicate requests.

### Error shape

PayChain errors use a structured response:

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request data",
    "details": [],
    "requestId": "req_..."
  }
}
```

Always log the `requestId` without logging secrets.

### Common error categories

| Category                        | Typical HTTP status | Retry?                                                |
| ------------------------------- | ------------------: | ----------------------------------------------------- |
| Validation error                |               `400` | No. Fix the request.                                  |
| Authentication error            |               `401` | No. Check API key and environment.                    |
| Permission error                |               `403` | No. Check key type and policy.                        |
| Not found                       |               `404` | No, unless the resource may be created later.         |
| Conflict / idempotency conflict |               `409` | Usually no. Inspect the existing request or resource. |
| Rate limited                    |               `429` | Yes, after backoff.                                   |
| Server or network error         |               `5xx` | Yes, with backoff and idempotency.                    |

### Error code catalog

Use the machine-readable `code` field for integration logic and the `message` for operator-facing context. Codes can expand over time, so integrations should handle unknown codes gracefully.

| Code                                     | Meaning                                                     | Typical action                                            |
| ---------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------- |
| `VALIDATION_ERROR`                       | Request body, query parameter, or field value is invalid.   | Fix the request before retrying.                          |
| `UNAUTHORIZED`                           | API key or session is missing or invalid.                   | Check environment and key storage.                        |
| `FORBIDDEN`                              | The key or user does not have permission for the action.    | Check key type, payout policy, or dashboard permission.   |
| `NOT_FOUND`                              | Resource does not exist or does not belong to the business. | Check IDs and environment.                                |
| `CONFLICT`                               | Request conflicts with current resource state.              | Fetch the resource and resolve state before retrying.     |
| `DUPLICATE_REQUEST`                      | A duplicate create request was detected.                    | Reuse idempotency keys and inspect the existing resource. |
| `IDEMPOTENCY_REQUEST_IN_PROGRESS`        | Another request with the same key is still processing.      | Wait, then fetch the resource or retry safely.            |
| `IDEMPOTENCY_KEY_MISMATCH`               | Same idempotency key was reused with a different payload.   | Use stable keys per unique business action.               |
| `RATE_LIMIT_EXCEEDED`                    | Too many requests in a window.                              | Back off and reduce polling.                              |
| `INSUFFICIENT_BALANCE`                   | Available business balance is not enough for the action.    | Reconcile balance or reduce the amount.                   |
| `WEBHOOK_EVENT_IN_FLIGHT`                | Webhook delivery is already being processed.                | Wait for terminal delivery state.                         |
| `WEBHOOK_EVENT_ALREADY_SCHEDULED`        | Retry or replay is already scheduled.                       | Do not schedule another duplicate attempt.                |
| `WEBHOOK_EVENT_ALREADY_DELIVERED`        | Webhook already reached a delivered state.                  | Treat as successful or replay only when supported.        |
| `WEBHOOK_EVENT_NOT_FOUND`                | Webhook event ID was not found.                             | Check environment and event ID.                           |
| `WEBHOOK_RETRY_COOLDOWN`                 | Retry is temporarily blocked by cooldown.                   | Retry later.                                              |
| `WEBHOOK_REPLAY_NOT_SUPPORTED`           | The event cannot be replayed.                               | Use normal retry or fetch the resource directly.          |
| `WEBHOOK_REPLAY_REQUIRES_TERMINAL_STATE` | Event must reach terminal state before replay.              | Wait for delivery to finish.                              |
| `WEBHOOK_REPLAY_COOLDOWN`                | Replay is temporarily blocked by cooldown.                  | Retry later.                                              |
| `BILLING_INVOICE_NOT_FOUND`              | Billing invoice was not found.                              | Check invoice ID and environment.                         |
| `BILLING_UNDERPAYMENT`                   | Billing payment was lower than required.                    | Pay the required invoice amount.                          |
| `BILLING_INVOICE_NOT_PAYABLE`            | Billing invoice is cancelled, expired, or not payable.      | Start a new billing or upgrade flow.                      |
| `BILLING_PAYMENT_REPLAY_CONFLICT`        | Billing payment conflicts with a prior payment record.      | Contact support with request ID.                          |
| `REDIS_COORDINATION_UNAVAILABLE`         | Temporary coordination dependency is unavailable.           | Retry later with idempotency.                             |
| `SERVICE_UNAVAILABLE`                    | PayChain dependency or service is temporarily unavailable.  | Retry later with backoff.                                 |
| `INTERNAL_SERVER_ERROR`                  | Unexpected PayChain error.                                  | Retry if safe, then contact support with request ID.      |

{% hint style="info" %}
**Support note:** When contacting support, include the request ID, resource ID, environment, and timestamp. Do not send API keys, webhook secrets, or raw credential headers.
{% endhint %}

### Idempotency keys

Send `Idempotency-Key` on mutating requests:

```http
Idempotency-Key: order_123_invoice
```

Use stable keys based on your own business object:

* `order_123_invoice`
* `payout_456`
* `customer_789_invoice_001`

Recommended formats:

| Action                           | Example key                        |
| -------------------------------- | ---------------------------------- |
| Create invoice for an order      | `order_123_invoice`                |
| Create invoice with payout route | `order_123_route_invoice`          |
| Create dynamic-recipient invoice | `order_123_dynamic_payout_invoice` |
| Quote withdrawal                 | `payout_456_quote`                 |
| Create withdrawal                | `payout_456_create`                |
| Execute approved contract action | `order_123_contract_action`        |

{% hint style="warning" %}
**Retry note:** Do not automatically retry non-idempotent `POST` requests unless you sent an idempotency key.
{% endhint %}

### Rate limits

Rate limits protect PayChain and merchants from accidental request bursts.

Recommended behavior:

* Back off on `429`.
* Respect retry headers if present.
* Avoid tight polling loops.
* Prefer webhooks for lifecycle changes.
* Use list pagination for reconciliation jobs.

### Safe retry policy

Retry:

* Network timeouts.
* `408`.
* `429`.
* `5xx`.

Do not blindly retry:

* Invalid request data.
* Wrong API key type.
* Rejected payout policy.
* Insufficient balance.
* Expired or cancelled invoices.
* Webhook events already delivered.

### Operational logging

Log:

* `requestId`.
* PayChain resource ID.
* Your order/customer reference.
* HTTP status.
* Error code.

Do not log:

* API keys.
* Webhook secrets.
* Request headers containing credentials.
* Raw payout destination secrets.
* Full raw webhook payloads in shared logs.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.paychainhq.io/paychainhq-documentation-page/developer-quickstart/errors-rate-limits-and-idempotency.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
