---
title: Idempotency
slug: idempotency
createdAt: 2026-11-02T00:00:00.000Z
---

Network failures and timeouts make it impossible to tell whether a request reached Forter. Idempotency lets you retry such requests safely: sending the same request twice with the same key has the same effect as sending it once, so a customer is never charged twice.

## Sending an Idempotency Key

Every `POST` request to the Orchestration API **must** include an `Idempotency-Key` header. Requests without one are rejected with `400 Bad Request`.

```bash
curl https://payments-sandbox.checkouttools.com/api/payments \
  -u "$FORTER_SITE_ID:$FORTER_SITE_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f0c8a8e-3b9c-4c4e-9a43-2a1f6a7d9e10" \
  -d '{ ... }'
```

Use a value with enough randomness to never collide, such as a V4 UUID. `GET` requests don't change anything and don't need a key.

## How Keys Are Matched

- Keys are scoped to your site and to the endpoint. The same key on `POST /api/payments` and on `POST /api/payments/{id}/capture` is treated as two different keys.
- When a request succeeds, Forter stores its response for **7 days**. Any later request with the same key on the same endpoint returns the stored response without running the operation again.
- Forter replays the stored response even if the new request has a different body. A key identifies one operation: never reuse it for a different request.

:::hint{type="warning"}
Generate a **new key for every distinct operation**, including the same operation on a different payment. For example, use a different key for each capture and for each partial refund. Reusing a key returns the response of the earlier request instead of performing the new operation.
:::

## What Is Stored

Only requests that complete are stored. A declined payment counts as completed: it returns status `failed`, and retrying it with the same key returns the same declined payment. To try the authorization again, use a new key. See [Declines and Retries](docId:24TiJspj41bFa8_yyScGt).

Requests that return an error, such as a validation error (`4xx`) or a server error (`5xx`), are **not** stored. You can fix the request and retry it with the same key.

## Concurrent Requests

If a request arrives while another request with the same key is still being processed, Forter rejects it with `409 Conflict` instead of running the operation twice. Wait briefly and retry with the same key. Once the first request completes, the retry returns its result.

## Recommended Retry Strategy

| Outcome                              | What to do                                                                                                                                                                                                                              |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Timeout or network error             | Retry with the **same** key and exponential back-off.                                                                                                                                                                                   |
| `409 Conflict`                       | The original request is still in progress. Retry with the **same** key after a short delay. If the `errorCode` is `PAYMENT_IDEMPOTENCY_CONFLICT`, the key is held by a pending refund of a different capture: retry with a **new** key. |
| `5xx`                                | Retry with the **same** key and exponential back-off.                                                                                                                                                                                   |
| `4xx` (other than `409`)             | Don't retry unchanged. Fix the request first. See [Errors](docId:4OHdkLQXF0xdlh_FrwLww).                                                                                                                                                |
| Success with payment status `failed` | The payment was declined. Retry only with a **new** key.                                                                                                                                                                                |

Store the idempotency key alongside the order in your system before sending the request. That way you can reuse it if your own process restarts mid-request.
