Error Codes
When a payment fails, Odus normalizes the processor-specific error into a standardized error code. Regardless of whether the payment was routed through Stripe, Adyen, PayPal, or any other gateway, the error response uses the same set of 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.
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 or hard_decline. |
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. |
Sensitive fields are only visible when using a secret API key. Requests made with a publishable key will not include these fields. Never expose sensitive fields to the customer — use safeErrorMessage instead of errorMessage.
Decline types
Every error code is classified as either a soft or hard decline:
| Type | Meaning |
|---|---|
soft_decline | The payment may succeed if retried. The issue is potentially temporary or resolvable by the customer. |
hard_decline | The payment will not succeed on retry with the same card. The card is blocked, restricted, or revoked. |
Error code reference
The table below lists every standardized error code, grouped by decline type.
Hard declines
These are non-recoverable. Do not retry the same card.
| Code | Description | Retry Strategy | Safe Message |
|---|---|---|---|
fraud_suspected | The processor flagged the transaction as potentially fraudulent. | never_retry | Your card was declined. Please contact your bank. |
card_restricted | The card is restricted — lost, stolen, or blocked by the issuer. | never_retry | Your card was declined. Please contact your bank. |
generic_hard_decline | Authorization was revoked or another non-recoverable decline occurred. | never_retry | Your card was declined. Please contact your bank. |
Soft declines — card issues
The customer can resolve these by updating their payment method.
| Code | Description | Retry Strategy | Safe Message |
|---|---|---|---|
insufficient_funds | The card does not have enough balance to cover the transaction. | retry_after_delay | Your card has insufficient funds. Please check your balance and try again. |
invalid_card_details | The card number, CVV, or other details are incorrect. | retry_with_new_payment_method | Your card details are invalid. Please check and try again. |
expired_card | The card is past its expiry date. | retry_with_new_payment_method | Your card has expired. Please update your payment method. |
authentication_failed | 3DS or other cardholder authentication failed. | retry_with_new_payment_method | Authentication failed. Please try again. |
transaction_not_permitted | The card or account does not allow this transaction type. | retry_with_new_payment_method | This transaction type is not permitted on your card. Please contact your bank or try 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 card issuer returned a generic error. Typically temporary. | retry_immediately | There was an error processing your card. Please try again later. |
generic_processor_error | The payment processor returned a generic error. | retry_immediately | There was an error processing your card. Please try again later. |
processor_rate_limited | The processor is rate-limiting requests. | retry_immediately | The payment processor is currently experiencing high traffic. Please try again in a few moments. |
processor_timeout | The processor did not respond in time. | retry_immediately | The payment processor is taking too long to respond. Please try again later. |
Soft declines — other
| Code | Description | Retry Strategy | Safe Message |
|---|---|---|---|
generic_decline | A general decline with no specific reason from the processor. | retry_after_delay | Your card was declined. Please contact your bank. |
cancelled | The customer cancelled the payment (e.g. closed the 3DS window). | retry_after_delay | The authentication was cancelled. |
not_supported | The payment method or currency is not supported by the processor. | retry_after_delay | This payment method is not supported. Please try a different one. |
internal_error | An error occurred within Odus while processing the payment. | retry_after_delay | Something went wrong. Please try again later. |
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 retry. The card is permanently blocked, restricted, or flagged for fraud. Retrying may incur network penalties. |
retry_with_new_payment_method | The current card cannot be used. Ask the customer to provide a different card or payment method. |
retry_after_delay | The issue may resolve on its own (e.g. insufficient funds). Wait before retrying. |
retry_immediately | The failure is transient (processor timeout, rate limit). Retry right away. |
For subscription payments, Odus uses the retry strategy automatically when scheduling retries. See the retry policy guide for details.
Example response
A failed authorization returns the outcome on the latest transaction:
{
"id": "pay_a1b2c3",
"status": "open",
"displayStatus": "failed",
"latestTransaction": {
"outcome": {
"status": "failure",
"errorCode": "insufficient_funds",
"errorMessage": "Your card has insufficient funds. Please check your balance and try again.",
"safeErrorMessage": "Your card has insufficient funds. Please check your balance and try again.",
"declineType": "soft_decline",
"retryStrategy": "retry_after_delay",
"processorCode": "card_declined",
"processorMessage": "Your card has insufficient funds.",
"specialCode": null
}
}
}
When using a publishable key, the response only includes status, safeErrorMessage, retryStrategy, and specialCode. Sensitive fields like errorCode, errorMessage, declineType, processorCode, and processorMessage are omitted.
Related
- Payment Statuses — lifecycle states and boolean flags on the payment object.
- Subscription Retry Policy — how Odus automatically retries failed subscription payments based on the retry strategy.