# Error Codes

When a payment fails, ODUS reports the reason as a standardized error code. Where the failure came from a payment processor, the processor-specific error is normalized into that same set — so regardless of whether the payment was routed through Stripe, Adyen, PayPal, or any other gateway, the error response uses the same codes. This lets you build a single error-handling path in your integration instead of mapping each processor's errors individually.

Error information is returned in the `outcome` object on the transaction.

note

These are **declines** — a request the API processed successfully, reporting a payment that did not go through. Usually the issuer refused it. Some codes instead report a payment that was never authorized against the payment method and cannot be by trying again; those carry `declineType` `not_attempted`. For requests the API refuses to process at all (validation failures, wrong resource state, missing permissions), see [Error Responses](https://docs.odus.com/docs/guides/api-features/errors.md).

## The outcome object

When a transaction fails, the response includes an `outcome` object with the following fields:

| Field              | Type             | Visibility | Description                                                                                                     |
| ------------------ | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------- |
| `status`           | `string`         | Public     | Operation result: `success`, `partial`, or `failure`.                                                           |
| `errorCode`        | `string`         | Sensitive  | Standardized error code from the table below.                                                                   |
| `errorMessage`     | `string`         | Sensitive  | Detailed error message. May contain sensitive information (e.g. fraud).                                         |
| `safeErrorMessage` | `string`         | Public     | Customer-safe error message that can be displayed in your UI.                                                   |
| `declineType`      | `string`         | Sensitive  | Either `soft_decline`, `hard_decline` or `not_attempted`.                                                       |
| `retryStrategy`    | `string`         | Public     | Recommended retry strategy for the client.                                                                      |
| `processorCode`    | `string`         | Sensitive  | Raw error code returned by the payment processor.                                                               |
| `processorMessage` | `string`         | Sensitive  | Raw error message returned by the payment processor.                                                            |
| `specialCode`      | `string \| null` | Public     | Special status codes such as `confirmation_requested` for 3DS redirects. `null` when no special status applies. |

warning

**Sensitive** fields are only visible when using a secret API key. They are not returned to a checkout key, which is what the Checkout SDK uses. Never expose sensitive fields to the customer — use `safeErrorMessage` instead of `errorMessage`.

## Decline types

Every error code carries a decline type: whether the payment method was refused, and whether repeating the same attempt could work.

| Type            | Meaning                                                                                                                                                                                                                                                                                                                                      |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `soft_decline`  | The payment may succeed if retried. The issue is potentially temporary or resolvable by the customer.                                                                                                                                                                                                                                        |
| `hard_decline`  | The payment method was refused and will not succeed on retry — it is blocked, restricted, or revoked.                                                                                                                                                                                                                                        |
| `not_attempted` | This payment was not authorized against the payment method, so the issuer refused nothing. The cause lies elsewhere — the stored payment method, your account, the request, the order, a policy rule, or a capture that already happened. Read `errorCode` for which, and the remedy. Repeating the same operation unchanged cannot succeed. |

## Error code reference

The table below lists every standardized error code, grouped by decline type.

Moving from v1

Eight codes and the `not_attempted` decline type are new in v2. v1 reports each of those payments as a broader code with a different decline type, and for three of them a different retry strategy. If your integration branches on `errorCode` or `declineType`, see [Codes added in v2](#codes-added-in-v2).

### Hard declines

These are non-recoverable. Do not retry the same payment method.

| Code                      | Description                                                                                                                     | Retry Strategy | Safe Message                                                     |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------- |
| `fraud_suspected`         | The processor flagged the transaction as potentially fraudulent.                                                                | `never_retry`  | The payment was declined. Please use a different payment method. |
| `card_restricted`         | The payment method or account is restricted — for a card, lost, stolen, or blocked by the issuer.                               | `never_retry`  | The payment was declined.                                        |
| `generic_hard_decline`    | Authorization was revoked or another non-recoverable decline occurred.                                                          | `never_retry`  | The payment was declined.                                        |
| `authentication_rejected` | The issuer's authentication server refused the authentication outright, or the card has already used up its allowed challenges. | `never_retry`  | The payment was declined. Please use a different payment method. |

### Soft declines — payment method issues

The customer can resolve these by correcting or changing their payment method.

| Code                        | Description                                                                                                                 | Retry Strategy                  | Safe Message                                                                                 |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------- |
| `insufficient_funds`        | The payment method does not have enough funds to cover the transaction.                                                     | `retry_after_delay`             | There aren't enough funds available for this payment. Please use a different payment method. |
| `invalid_card_details`      | The payment details were rejected as invalid — for a card, the number, security code or expiry.                             | `retry_with_new_payment_method` | Some of the payment details are incorrect. Please check them and try again.                  |
| `expired_card`              | The payment method is past its expiry date.                                                                                 | `retry_with_new_payment_method` | This payment method has expired. Please use a different one.                                 |
| `authentication_failed`     | Authentication was required but could not be completed — a wrong one-time code, or a card the issuer will not authenticate. | `retry_with_new_payment_method` | The payment couldn't be verified. Please try again or use a different payment method.        |
| `transaction_not_permitted` | The payment method or account does not allow this transaction type.                                                         | `retry_with_new_payment_method` | The payment was declined. Please use a different payment method.                             |

### Soft declines — processor / issuer errors

These are transient. Retrying may succeed without any customer action.

| Code                         | Description                                                                                    | Retry Strategy      | Safe Message                                                                                        |
| ---------------------------- | ---------------------------------------------------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------- |
| `generic_issuer_error`       | The issuer or funding provider returned a generic error. Typically temporary.                  | `retry_immediately` | We couldn't confirm this payment in time. Please check whether it went through before trying again. |
| `generic_processor_error`    | The payment processor returned a generic error.                                                | `retry_immediately` | We couldn't confirm this payment in time. Please check whether it went through before trying again. |
| `processor_rate_limited`     | The processor is rate-limiting requests.                                                       | `retry_immediately` | We're busy right now. Please try again in a few moments.                                            |
| `processor_timeout`          | The processor did not respond in time.                                                         | `retry_immediately` | We couldn't confirm this payment in time. Please check whether it went through before trying again. |
| `authentication_unavailable` | Authentication never ran — the issuer's authentication server timed out or failed technically. | `retry_immediately` | Something went wrong. Please try again.                                                             |
| `authentication_required`    | The issuer requires the payment to be authenticated and refused it without 3D Secure.          | `retry_immediately` | This payment needs to be verified. Please try again to continue.                                    |

info

`authentication_cancelled`, `authentication_rejected` and `authentication_unavailable` each describe a specific way a 3D Secure authentication did not succeed. A processor may report all of them under a single refusal reason — Adyen's "3D Not Authenticated" is one — so ODUS separates them from the authentication result on the response, because the right response differs: a customer who dismissed the challenge stopped the payment themselves and can start it again, an authentication server that never answered can be retried without involving them, and an issuer that refused outright must not be retried at all. `authentication_cancelled` is listed under [Soft declines — other](#soft-declines--other), alongside `cancelled`, because the two describe the same customer action.

`authentication_required` is the one case where authentication has not happened yet rather than failed. The issuer refused an exemption and wants the payment authenticated, so retrying it with 3D Secure is the documented remedy — there is nothing wrong with the payment method, and asking the customer to change it will not help.

`authentication_failed` is the general case, and is not limited to 3D Secure: it also covers a card the issuer will not authenticate at all, and approval failures on other payment methods such as a PayPal billing agreement.

### Soft declines — other

| Code                       | Description                                                                                                                                                                                                                          | Retry Strategy      | Safe Message                                                                      |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | --------------------------------------------------------------------------------- |
| `generic_decline`          | A general decline with no specific reason from the processor.                                                                                                                                                                        | `retry_after_delay` | The payment was declined. Please try again or use a different payment method.     |
| `cancelled`                | The customer cancelled the payment, at a 3DS challenge or at another approval step such as a PayPal approval. Where the processor identifies a cancelled 3DS challenge specifically, `authentication_cancelled` is reported instead. | `retry_after_delay` | The payment was cancelled. You can try again when you're ready.                   |
| `authentication_cancelled` | The customer dismissed the authentication challenge. Reported instead of `cancelled` where the processor identifies a cancelled 3DS challenge specifically; both describe the same customer action and carry the same strategy.      | `retry_after_delay` | The payment was cancelled. You can try again when you're ready.                   |
| `not_supported`            | The payment method or currency is not supported by the processor.                                                                                                                                                                    | `retry_after_delay` | This payment method isn't available for this payment. Please use a different one. |
| `internal_error`           | An error occurred within ODUS while processing the payment.                                                                                                                                                                          | `retry_after_delay` | Something went wrong. Please try again.                                           |

## Codes added in v2

Some of the codes above cover more than one outcome, and the advice attached to them cannot be true of all of them at once. `generic_hard_decline` reports a payment that has *already been captured* alongside genuine refusals, so "use a different payment method" invites a second charge for the same order. `card_restricted` reports a problem with the store's own processor account alongside a blocked card, so no payment method your customer chooses gets past it. And `generic_processor_error` and `generic_issuer_error` mean the payment was left unresolved and needs reconciling before a retry — but they also carry requests that were refused outright, where nothing is unresolved at all.

v2 gives each of those outcomes its own code, and adds two more — `payment_method_unusable`, for a stored payment method that can no longer be charged, and `stored_credential_revoked`, for one the processor now refuses to let you charge. **v1 is unchanged**: it keeps reporting all eight as the broader code in the last column, with that code's decline type and retry strategy.

All eight are `not_attempted` with `never_retry`. None is a decision by the issuer about your customer's payment method, and none is resolved by another attempt at the same thing. For all but `payment_method_unusable` and `stored_credential_revoked`, the remedy is in the store's hands rather than the customer's.

| Code (v2)                      | Description                                                                                                                                                                                                           | Safe Message                                                        | Reported in v1 as           |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------- |
| `payment_method_unusable`      | The stored payment method can no longer be charged and has to be replaced. Ask the customer for a new one; retrying this one cannot succeed.                                                                          | The payment was declined. Please use a different payment method.    | `generic_hard_decline`      |
| `stored_credential_revoked`    | Permission to charge a stored payment method has been withdrawn, so the processor refuses it without asking the funding source. Ask the customer to authorize a new payment method; this one cannot be charged again. | The payment was declined. Please use a different payment method.    | `generic_hard_decline`      |
| `already_captured`             | The payment has already been taken. Nothing is owed, and charging again would take it twice.                                                                                                                          | This payment has already gone through. You don't need to pay again. | `generic_hard_decline`      |
| `merchant_account_restricted`  | Your account with the processor is locked, closed or restricted, so it cannot accept payments.                                                                                                                        | This store can't take this payment right now.                       | `card_restricted`           |
| `merchant_configuration_error` | Your account is not set up to accept this payment — the payment method, country or currency is not enabled on it.                                                                                                     | This store can't take this payment right now.                       | `generic_issuer_error`      |
| `invalid_request`              | The processor refused the request as malformed or invalid. It never reached an authorization, so nothing is unresolved.                                                                                               | This payment can't be completed.                                    | `generic_processor_error`   |
| `blocked_by_policy`            | A risk or compliance rule blocked the payment. It is not a fraud signal about your customer's payment method.                                                                                                         | The payment was declined.                                           | `fraud_suspected`           |
| `order_not_permitted`          | Something about the order is not accepted — where it ships to, what it contains, or how its amount is expressed.                                                                                                      | This payment can't be completed.                                    | `transaction_not_permitted` |

info

`invalid_request` and `merchant_configuration_error` are the definitive counterparts to `generic_processor_error` and `generic_issuer_error`. Where those two warn you that the payment may have gone through and needs checking before a retry, these two tell you it did not: the request was refused before any money moved, so you can act on the failure immediately instead of reconciling first.

## Retry strategies

Each error code maps to a recommended retry strategy. Use this to decide how your integration should respond to a failure.

| Strategy                        | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `never_retry`                   | Do not repeat this operation unchanged — it cannot succeed. Check `errorCode` for the remedy: a `hard_decline` means the payment method is blocked, restricted or flagged for fraud and retrying it may incur network penalties, while a `not_attempted` code points at the stored method, your account, or the request instead.                                                                                                                                                                                                                                                                                                                              |
| `retry_with_new_payment_method` | The current payment method cannot be used. Ask the customer to provide a different one.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `retry_after_delay`             | The issue may resolve on its own (e.g. insufficient funds). Wait before retrying.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `retry_immediately`             | The failure is transient and no decision was reached. Your integration may retry at once **only** after `authentication_required`, `authentication_unavailable` or `processor_rate_limited`, where the request is known to have been turned away before authorisation — for `authentication_required`, retry with 3D Secure rather than unchanged. The other codes carrying this strategy — `processor_timeout`, `generic_processor_error`, `generic_issuer_error` — cover cases the processor never resolved, so the payment may already have been processed: check its current status and reconcile before retrying, or your customer can be charged twice. |

tip

For subscription payments, ODUS uses the retry strategy automatically when scheduling retries. When a subscription renewal fails with `authentication_unavailable` or `processor_rate_limited`, ODUS also resends it unchanged within minutes, before your retry policy starts. See the [retry policy guide](https://docs.odus.com/docs/guides/subscriptions/retry-policy.md#payments-that-never-reached-a-decision) for details.

## Example response

A failed authorization returns the `outcome` on the latest transaction:

Failed payment response (secret key)

```
{

  "id": "pay_a1b2c3",

  "status": "open",

  "displayStatus": "failed",

  "latestTransaction": {

    "outcome": {

      "status": "failure",

      "errorCode": "insufficient_funds",

      "errorMessage": "The payment method has insufficient funds.",

      "safeErrorMessage": "There aren't enough funds available for this payment. Please use a different payment method.",

      "declineType": "soft_decline",

      "retryStrategy": "retry_after_delay",

      "processorCode": "card_declined",

      "processorMessage": "Your card has insufficient funds.",

      "specialCode": null

    }

  }

}
```

info

When read with a checkout key, the response only includes `status`, `safeErrorMessage`, `retryStrategy`, and `specialCode`. Sensitive fields like `errorCode`, `errorMessage`, `declineType`, `processorCode`, and `processorMessage` are omitted.

## Related

* [Payment Statuses](https://docs.odus.com/docs/guides/payments/payment-statuses.md) — lifecycle states and boolean flags on the payment object.
* [Subscription Retry Policy](https://docs.odus.com/docs/guides/subscriptions/retry-policy.md) — how ODUS automatically retries failed subscription payments based on the retry strategy.
