# Error handling and retry strategy Payments Direct 2.0 returns a [standardized error response](/products/payments-direct-2/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/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/api-docs/error-codes/api-payment-failures) and [API error codes reference](/products/payments-direct-2/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/api-docs/error-codes/api-payment-failures). For background on payment states and the payment lifecycle, see [Payment states](/products/payments-direct-2/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/api-docs/error-codes/api-errors). - For payment-specific failure codes, see [API payment failures](/products/payments-direct-2/api-docs/error-codes/api-payment-failures). - For guidance on tracking payment state changes without polling, see [Notification webhooks](/products/payments-direct-2/api-docs/best-practices/notification-webhooks). - To poll for payment updates instead of using webhooks, see [Polling](/products/payments-direct-2/api-docs/best-practices/polling). - For access token generation and caching details, see [Authentication](/products/payments-direct-2/api-docs/best-practices/authentication). - For payment state definitions and lifecycle details, see [Payment states](/products/payments-direct-2/api-docs/concepts/payment-states).