> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.suby.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency

> Safely retry POST requests with an Idempotency-Key so a timeout or double-submit never charges twice.

Network calls fail in ways you can't tell apart from the client: a request may
time out **after** the server created a payment but **before** you got the
response. Retrying blindly would create a second charge. **Idempotency keys**
make a retry safe — the server recognises the repeat and returns the **original
response** instead of executing the request again.

## How it works

Send an **`Idempotency-Key`** header on any `POST` request:

```bash theme={null}
curl https://api.suby.fi/v3/payments \
  -H "X-Suby-Api-Key: sk_live_…" \
  -H "Idempotency-Key: 5f3b9c2e-1a4d-4f2b-9c31-7e2a1b6d8c04" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 999, "currency": "EUR", "method": "CARD",
        "customerId": "cus_…", "card": { "tokenizedInstrument": "tok_…" } }'
```

* The **first** request with that key runs normally; its response (status + body)
  is stored.
* Any **retry** with the **same key** short-circuits and returns that stored
  response verbatim — same status code, same body — **without** re-executing. No
  second payment, no second subscription.

<Note>
  The key is **optional** — omit it and the endpoint behaves exactly as before.
  We strongly recommend sending one on every state-changing `POST`
  (payments, subscriptions, setup-intents, checkout sessions, refunds…).
</Note>

## Choosing a key

* Generate a **unique** value per logical operation — a **UUID v4** is ideal.
* Reuse the **same** key when you retry that operation, and a **new** key for a
  genuinely new operation.
* Max length **255 characters**.

## Scope & lifetime

| Property      | Behaviour                                                                                                         |
| ------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Scope**     | Per account **and** environment — a `sk_sandbox` key and a `sk_live` key never collide, even with the same value. |
| **Binding**   | The key is tied to the exact request (method + path + body).                                                      |
| **Retention** | **24 hours.** After that the key is free to reuse.                                                                |
| **Methods**   | `POST` only. `GET`/`DELETE` are already idempotent; `PATCH` is not deduplicated.                                  |

## Responses to a repeat

| Situation                                     | Result                                                                          |
| --------------------------------------------- | ------------------------------------------------------------------------------- |
| Same key, **same** request                    | The **original response is replayed** (same status + body).                     |
| Same key, **different** request body or path  | `422` `IDEMPOTENCY_KEY_CONFLICT` — the key was already used for something else. |
| Same key, **first request still in progress** | `409` `IDEMPOTENCY_KEY_CONFLICT` — wait and retry.                              |
| Same key, **older than 24 h**                 | Treated as new — the operation executes again.                                  |

Server errors (`5xx`) are **not** cached: the key is released so you can safely
retry the operation. Client outcomes (`2xx` and `4xx`) are cached and replayed.

## Idempotency-Key vs. externalRef

These are different tools — don't conflate them:

* **`Idempotency-Key`** (header) — a per-**request** token whose only job is to
  deduplicate a retried HTTP call. Unique per attempt, expires in 24 h.
* **`externalRef`** (body field on `POST /v3/payments`) — your **business**
  reference (an order id) for reconciliation and lookup. It can legitimately
  appear on several payments (e.g. a retried order after a decline) and is
  **not** used for deduplication.

Use `Idempotency-Key` to make retries safe; use `externalRef` to tie a payment
back to your own records.
