# Error handling and retry strategy

Payments Direct 2.0 returns a [standardized error response](/products/payments-direct-2/v2026.03/api-docs/error-codes/payments-direct-api-errors) for every failed API request. Building a reliable integration means classifying errors correctly, retrying safely, and escalating promptly when retrying won't help.

This topic covers:

- How to classify errors using the `code` and `type` fields
- Which errors are safe to retry, and how to retry them
- Recommended exponential backoff parameters
- Handling authentication errors
- Handling payment failures (FAILED, DECLINED, RETURNED states)
- What to log and monitor


## Classify before you act

Every error response includes a `code` field (for example, `USR_067`) and a `type` field (for example, `USER_ERROR`). Always use the `code` field as your primary signal, not the HTTP status code alone. Two errors can share an HTTP status but require completely different handling.

### Error type reference

| Type | Prefix | What it means | General action |
|  --- | --- | --- | --- |
| `USER_ERROR` or `NOT_FOUND` | `USR_` | The request itself is the problem: missing fields, invalid values, or a resource that doesn't exist. | Fix the request and resubmit. Do not retry the same request. |
| `AUTH_ERROR` | `AUTH_` | The request was rejected due to an authentication or authorization problem. | See [Handling authentication errors](#handling-authentication-errors) below. |
| `SYSTEM_ERROR` | `SYS_` | An internal error occurred in Ripple's infrastructure. | Retry with exponential backoff. Escalate to Ripple technical support if the condition persists. |
| `CONFIGURATION_ERROR` | `CFG_` | An account or service configuration issue prevents the request from completing. | Do not retry. Contact Ripple technical support. |


## Transient vs. permanent errors

Before retrying, confirm that the error is transient (a condition that may resolve itself) rather than permanent.

| Error type | HTTP status | Retryable? | Recommended action |
|  --- | --- | --- | --- |
| `USER_ERROR` | 400, 404, 415 | No | Fix the request and resubmit. |
| `USER_ERROR` | 402 | Not until resolved | Resolve the underlying condition (insufficient balance, credit limit, or past-due invoice) before retrying. |
| `NOT_FOUND` | 404 | No | Verify the resource ID and resubmit. |
| `AUTH_ERROR` | 401 | Yes (after token refresh) | Regenerate your access token and retry. |
| `AUTH_ERROR` | 403 | No | Verify your token has the required scopes. Contact Ripple technical support if the issue persists. |
| `SYSTEM_ERROR` | 500 | Yes (with backoff) | Retry with exponential backoff. Contact Ripple technical support if the issue persists. |
| `CONFIGURATION_ERROR` | 500 | No | Contact Ripple technical support. |


## Retry strategy

### Exponential backoff with jitter

When a request fails with a retryable error (`SYSTEM_ERROR` / 500, or `AUTH_ERROR` / 401 after a token refresh), use exponential backoff with jitter rather than retrying immediately or at a fixed interval. Immediate or synchronized retries amplify load on an already stressed system and can trigger rate limiting.

Recommended approach:

1. On the first failure, wait a short base interval.
2. On each subsequent failure, double the wait time.
3. Add random jitter (±10–20% of the interval) to desynchronize retries across clients.
4. Cap the maximum wait at a reasonable ceiling.
5. After a configurable number of attempts, stop retrying and alert your on-call team.


**Example backoff schedule:**

| Retry attempt | Base wait | With jitter (±20%) |
|  --- | --- | --- |
| 1 | 2s | 1.6s – 2.4s |
| 2 | 4s | 3.2s – 4.8s |
| 3 | 8s | 6.4s – 9.6s |
| 4 | 16s | 12.8s – 19.2s |
| 5 | 32s | 25.6s – 38.4s |
| 6+ | 60s (cap) | 48s – 72s |


### Use a maximum retry count

Set a hard limit on the number of retries (for example, 5 attempts). After that limit is reached without a successful response, stop retrying, record the failure, and alert your team. Continuing to retry indefinitely can mask a persistent issue and delay investigation.

## Idempotency and safe retries

Before retrying a mutating request (such as creating a payment), check whether the original request may have been received and processed by Ripple despite returning an error. A network timeout, for example, does not mean the payment was not created.

**Best practice:** Use a client-assigned `internalId` or idempotency mechanism when creating payments. If you retry a payment creation request, include the same `internalId` as the original request. Ripple will return the existing payment if it was already created, preventing duplicate payments.

Do not blindly retry payment creation
If you receive a timeout or connection error on a create payment request, do not immediately retry without first checking whether the payment was created. Use `GET /v3/payments` with your `internalId` to check before submitting again.

## Handling authentication errors

### 401 Unauthorized (AUTH_001, AUTH_003)

A 401 error typically means your access token is expired or invalid. Access tokens have a 1-hour TTL.

**Recommended handling:**

1. Generate a new access token using your `client_id` and `client_secret`.
2. Retry the original request with the new token.
3. Do not cache tokens beyond their `expires_in` value.


For guidance on token generation and caching, see [Authentication](/products/payments-direct-2/v2026.03/api-docs/best-practices/authentication).

### 403 Forbidden (AUTH_002)

A 403 error means your token is valid but lacks the required scopes for the requested operation.

**Recommended handling:**

- Do not retry with the same token. A new token with the same credentials will have the same scopes.
- Review the required scopes for the operation in the API reference.
- Contact Ripple technical support if you believe your credentials should have the required permissions.


### 403 Unauthorized (AUTH_051)

AUTH_051 occurs when a request references a payment created by a different organization. This is a permanent error. Do not retry.

## Handling payment failures

Payment failures are distinct from API errors. A payment failure occurs after a payment is created successfully (HTTP 201) but later transitions to a terminal state of `FAILED`, `DECLINED`, or `RETURNED`. These are not errors in the API response. They are `paymentState` values you observe when polling or receiving webhooks.

How to detect a payment failure
Payment failure details, including the error code and reason, are available on the payment object at `GET /v3/payments/{paymentId}` after the payment reaches a terminal state. Webhook payloads include the new `paymentState` but not the error code. Always fetch the full payment record to get failure details.

### Terminal states

| State | Retryable? | Action |
|  --- | --- | --- |
| `FAILED` | Possibly | Fetch the payment record to get the error code. Check the [API payment failures reference](/products/payments-direct-2/v2026.03/api-docs/error-codes/api-payment-failures) and [API error codes reference](/products/payments-direct-2/v2026.03/api-docs/error-codes/api-errors) for the specific code. Some `FAILED` states are caused by transient issues and can be retried with a new payment; others indicate a permanent condition. |
| `DECLINED` | Not until resolved | Fetch the payment record to get the error code. `DECLINED` usually indicates a business rule violation or account condition (insufficient balance, credit limit, etc.) that must be resolved before resubmitting. |
| `RETURNED` | Consult Ripple | The payment was returned by a downstream institution after completing. This is not a retry scenario. Contact Ripple technical support to investigate the return reason. |


For a full list of payment failure codes and their descriptions, see [API payment failures](/products/payments-direct-2/v2026.03/api-docs/error-codes/api-payment-failures).

For background on payment states and the payment lifecycle, see [Payment states](/products/payments-direct-2/v2026.03/api-docs/concepts/payment-states).

## What to log and monitor

Log the following fields from every error response:

- `code` — the specific error code; use this for alerting rules and dashboards
- `type` — the error category; use this to route to the correct handler
- `status` — the HTTP status code
- `timestamp` — the time the error occurred in the upstream system
- The API endpoint and HTTP method that returned the error
- Your request ID or correlation ID (if your integration generates one)


### Alerting recommendations

| Condition | Alert priority |
|  --- | --- |
| Any `CONFIGURATION_ERROR` (`CFG_`) | High — these require Ripple involvement and will not self-resolve |
| Repeated `SYSTEM_ERROR` (`SYS_`) from the same endpoint | Medium — investigate after retry budget is exhausted |
| `AUTH_ERROR` 403 (`AUTH_002`, `AUTH_051`) | Medium — may indicate a configuration change or credential scope issue |
| Sustained `USER_ERROR` 402 (`USR_062`–`USR_067`) | Medium — indicates an account condition (balance, limits, invoice) requiring attention |


## Next steps

- For the complete list of error codes, descriptions, and HTTP statuses, see [API error codes](/products/payments-direct-2/v2026.03/api-docs/error-codes/api-errors).
- For payment-specific failure codes, see [API payment failures](/products/payments-direct-2/v2026.03/api-docs/error-codes/api-payment-failures).
- For guidance on tracking payment state changes without polling, see [Notification webhooks](/products/payments-direct-2/v2026.03/api-docs/best-practices/notification-webhooks).
- To poll for payment updates instead of using webhooks, see [Polling](/products/payments-direct-2/v2026.03/api-docs/best-practices/polling).
- For access token generation and caching details, see [Authentication](/products/payments-direct-2/v2026.03/api-docs/best-practices/authentication).
- For payment state definitions and lifecycle details, see [Payment states](/products/payments-direct-2/v2026.03/api-docs/concepts/payment-states).