# Errors and rate limits

The error envelope, what each code range means, and how to handle rate limits.

## The error envelope

API request errors return a consistent object. An error can occur before or after submission to the payment gateway:

```json
{
  "code": 2000,
  "status": "Rejected",
  "message": "One or more validation errors occurred.",
  "traceId": "1-6838bcce-5c0074e82ac7170d4f990d87",
  "timestamp": "2025-05-29T20:00:15.5752808Z",
  "errorDetails": [
    {
      "code": 2000,
      "field": "Amount",
      "message": "Request validation failed",
      "details": "The Amount field must be a positive number between 1 and 999,999,999."
    }
  ]
}
```

Always log `traceId`. It is the single fastest way for support to find your request.

## status is what tells you whether to retry

`status` is the execution status of the request against the payment gateway, and it is the field that decides your retry behaviour.

| Status        | Meaning                                                                                      | Safe to retry?                    |
| ------------- | -------------------------------------------------------------------------------------------- | --------------------------------- |
| `Completed`   | The request completed successfully.                                                          | n/a                               |
| `Rejected`    | The request was rejected before execution or by the gateway.                                 | Correct the cause before retrying |
| `Interrupted` | The request was interrupted and the final status is unknown. Side effects may have occurred. | **No** - check first              |
| `Unknown`     | Status could not be determined.                                                              | **No** - check first              |

> **Important:**
>
> `Interrupted` does not mean the transaction failed. The operation might already have succeeded. Before retrying, resolve the real outcome with [`GET /transactions`](https://doura.ca/docs/payments/transaction-retrieval.md), filtering by that operation's `referenceId`. Keep the same reference for any retry to preserve duplicate prevention.

## Duplicate-reference errors

The API uses `referenceId` to prevent duplicate transaction requests. The duplicate-reference validation check returns HTTP `400`, code `2000`, and status `Rejected` when the reference is already in use for that merchant. The error indicates that a request already exists; it does not tell you whether that earlier request succeeded, failed, or is still processing. The API does not replay its response.

Retrieve the existing transaction by the request's own `referenceId` and inspect its outcome. Do not change the reference just to get past this error: a new reference identifies a separate operation and can cause the operation to execute again. See [Preventing duplicate requests](https://doura.ca/docs/payments/transaction-retrieval.md#preventing-duplicate-requests).

## Error code ranges

`code` is a numeric error code, grouped into ranges so you can branch on the range rather than on every individual value.

| Range         | Category                         | Typical handling                                                                                             |
| ------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `1000`-`1999` | Authentication and authorization | Correct the API key, resource ownership, or feature access before retrying.                                  |
| `2000`-`2999` | Request validation               | Fix the request. `errorDetails` names the offending field. Not retryable.                                    |
| `3000`-`3499` | Server and session errors        | Reconcile any earlier submission before replacing an expired or used session. Other errors may be transient. |
| `3500`-`3599` | Billing subscription errors      | Inspect the subscription state before retrying.                                                              |
| `4000`-`4999` | Resource not found               | The ID does not exist, or belongs to another merchant. Not retryable.                                        |
| `6000`-`6999` | SDK and client-side errors       | See [SDK error codes](https://doura.ca/docs/payment-sdk/error-codes.md).                                                          |
| `9000`-`9999` | Unhandled errors                 | Contact support with the `traceId`.                                                                          |

The complete enumeration, with a description for every individual code, is on the `Error` schema in the API reference.

## Transaction result codes are different

Transaction responses use `resultCode` and, when available, `resultText`. These are distinct from `code` and `message` in an error envelope:

* `0` - the transaction request was successful. Check each `transactionResponse`'s `responseCode` to determine whether the transaction was approved or declined.
* `1010` - the transaction request is pending customer interaction.
* `2014` - the payment method or card brand is not enabled for the merchant. `resultText` is the message, such as `Visa cards are not enabled for this merchant.`, in sentence case; every other `resultText` on a stored transaction is uppercase. A token sale or authorization also returns it as an HTTP `400` error with code `2014`.
* `4100` - a token payment requires a cardholder challenge. Follow `threeDsChallenge.redirectUrl` and reconcile the outcome before retrying.
* `4001` - the transaction is being processed. Returned for asynchronous bank transfer payment methods such as [ACH](https://doura.ca/docs/payments/ach.md) while awaiting a final outcome.
* `1003`, `1005`, `3002`, `3005`, `3006`, `3009` - terminal-specific conditions on [card present](https://doura.ca/docs/payments/card-present.md) transactions, such as a busy, low-battery, or disconnected terminal.
* `6800`-`6802` - [3D Secure](https://doura.ca/docs/hosted-payment-form/3d-secure.md) outcomes on declined card-not-present transactions.
* `6900`-`6902` - hosted payment form and Payment SDK card outcomes: an issuer decline, a gateway failure, or a missing payment token. See [SDK error codes](https://doura.ca/docs/payment-sdk/error-codes.md#hosted-form-gateway-outcomes-6900-6999).

> **Note:**
>
> `resultCode: 0` means the *request* succeeded, not that the money moved. A declined sale is a successful request with a declining `responseCode` inside `transactionResponses`. Use `responseCode` to decide whether the payment was approved; see [Transaction response codes](#transaction-response-codes).

The full `resultCode` enumeration is on the `ResultCode` schema in the API reference.

## Transaction response codes

`responseCode` is the field that tells you whether a transaction succeeded. Each entry in `transactionResponses` carries one. The HTTP status, `resultCode`, and `status` do not tell you whether the payment was approved.

A transaction succeeded only when `status` is `Completed` and `transactionResponses` contains an entry with `responseCode` `1` (approved) or `2` (partially approved). Every other code means the payment was not approved:

* **Declined** - the issuer or bank refused the payment. Do not retry with the same payment details; ask the customer for another payment method.
* **Canceled** - the customer or merchant stopped the transaction. No money moved.
* **Pending** - the outcome is not final. Do not treat it as a success or a failure; wait for the [webhook](https://doura.ca/docs/webhooks.md) or [query the transaction](https://doura.ca/docs/payments/transaction-retrieval.md) again.
* **Unknown** (`0`) - no specific code was reported. While `status` is not `Completed`, treat it like Pending. On a `Completed` transaction it is a failure, not an approval: read `resultCode` and `resultText` for the reason, for example `2014` or `6901`.
* **Error** - processing failed for a technical or request reason. Fix the cause before you retry.

`transactionResponses` is empty while processing is incomplete, so an empty array is not a decline.

On a [`Chargeback`](https://doura.ca/docs/payments/ach.md#chargebacks) transaction, `responseCode` `1` means the chargeback took effect: the money went back to the payer. It is not a payment to you. For a partial approval, only `amountApproved` was approved; collect the balance another way.

Branch on the number; the name is for reference only.

| Code | Name                      | Outcome  | Meaning                                                                                              |
| ---- | ------------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `0`  | `UNKNOWN`                 | Unknown  | No specific code was reported. On a `Completed` transaction, read `resultCode` for the reason.       |
| `1`  | `APPROVED`                | Approved | The transaction was approved.                                                                        |
| `2`  | `PARTIAL_APPROVED`        | Approved | The transaction was approved for less than the requested amount. Read `amountApproved`.              |
| `10` | `DECLINED`                | Declined | The issuer or bank declined the transaction. On card present, the terminal can also decline offline. |
| `11` | `INSUFFICIENT_FUNDS`      | Declined | The account does not have sufficient funds.                                                          |
| `12` | `CARD_EXPIRED`            | Declined | The card has expired.                                                                                |
| `13` | `FRAUD_SUSPECTED`         | Declined | The transaction was flagged for potential fraud.                                                     |
| `14` | `INVALID_PAYMENT_INFO`    | Declined | The payment information, such as the card number, is invalid.                                        |
| `15` | `LIMIT_EXCEEDED`          | Declined | A limit, such as the credit limit, was reached.                                                      |
| `16` | `VERIFICATION_REQUIRED`   | Declined | Additional customer verification, such as 3D Secure, is required.                                    |
| `17` | `AUTHENTICATION_FAILED`   | Declined | The PIN was incorrect or authentication failed.                                                      |
| `18` | `POLICY_VIOLATION`        | Declined | The transaction violates the payment gateway's terms of service or policies.                         |
| `19` | `BATCH_EMPTY`             | Error    | The batch for processing is empty.                                                                   |
| `20` | `RECORD_NOT_FOUND`        | Error    | The referenced transaction was not found, for example when voiding.                                  |
| `21` | `ALREADY_VOIDED`          | Error    | The transaction is already voided.                                                                   |
| `50` | `NETWORK_ERROR`           | Error    | A network or communication error occurred with the payment gateway.                                  |
| `51` | `INVALID_ECR_PARAMETER`   | Error    | A payment terminal or system-specific error occurred.                                                |
| `52` | `NOT_SUPPORTED`           | Error    | The requested operation is not supported.                                                            |
| `53` | `BUSY_OR_UNAVAILABLE`     | Error    | The payment terminal, payment app, or system is busy or unavailable.                                 |
| `54` | `CANCELLED`               | Canceled | The user or merchant canceled the transaction.                                                       |
| `55` | `USER_CANCELLED`          | Canceled | The customer canceled the transaction.                                                               |
| `56` | `TIMED_OUT_ON_USER_INPUT` | Error    | The customer did not complete the transaction in time.                                               |
| `57` | `PENDING`                 | Pending  | The transaction is awaiting an asynchronous outcome, as with [ACH](https://doura.ca/docs/payments/ach.md).                |

The same list is on the `ResponseCode` schema in the API reference.

## HTTP status codes

| Code  | Meaning                                                                                                                                                                |
| ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200` | The request was processed. Inspect the body for the transaction or error result.                                                                                       |
| `400` | Request validation failed. See `errorDetails`.                                                                                                                         |
| `401` | The API key is missing or invalid, or does not authorize the requested resource.                                                                                       |
| `403` | Settlement reporting access is not enabled. Contact support to enable access; see [Reporting access](https://doura.ca/docs/settlement-reporting/reporting.md#access-and-data-availability). |
| `404` | The resource does not exist, or is not owned by your merchant.                                                                                                         |
| `409` | A resource or transaction is not ready yet (`3400`, `3401`, or `4001` in the error envelope).                                                                          |
| `500` | Server or processing error. Check execution `status` before deciding whether to retry.                                                                                 |
| `429` | You have exceeded the rate limit.                                                                                                                                      |

## Rate limiting

If you receive `429 Too Many Requests`, pause before retrying. Honor `Retry-After` when supplied; otherwise use exponential backoff. The public API does not guarantee `X-RateLimit-Limit`, `X-RateLimit-Remaining`, or `X-RateLimit-Reset` headers.
