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

# Idempotency

> Retry any request safely without creating duplicate charges

Networks fail. Processes crash. When a create call times out — a payment, a
hosted payment page payment, or a refund — you cannot tell whether the
operation happened. Idempotency keys make the answer not matter: retry safely,
and you will never charge (or refund) a customer twice.

## How it works

Every `POST` request requires an `Idempotency-Key` header — a string of your
choice, up to 255 characters:

```bash theme={null}
curl -X POST https://api.flowlix.dev/v1/payments/hpp \
  -H "Authorization: Bearer api_test_sk_abc123def456" \
  -H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
```

A key is unique per **API key and operation** (payment create, hosted payment
create, refund create). On a retry, the request body is compared with the
original request:

| Situation                                        | Result                                                                              |
| ------------------------------------------------ | ----------------------------------------------------------------------------------- |
| First request with a key                         | Processed normally                                                                  |
| Retry: same key, **same body**                   | The original response is replayed — no new object is created                        |
| Retry: same key, **different body** (any field)  | `409 Conflict` with `error.code: "idempotency_key_reused"`                          |
| Retry while the first request is still in flight | `409 Conflict` with `error.code: "idempotency_key_in_use"` — wait briefly and retry |
| Missing `Idempotency-Key` header                 | `400` with `error.code: "parameter_missing"`                                        |

Additional rules:

* Only **successful (2xx)** responses are cached. If a request failed with an
  error, retrying with the same key executes it again.
* Keys expire after **24 hours**. After expiry, a reused key behaves like a
  brand-new one.
* Keys are scoped to your API key (and therefore to its test/live mode) and to
  the operation type: the same key value sent to `POST /v1/payments` and
  `POST /v1/refunds` identifies two independent operations.

## Choosing keys

<CardGroup cols={2}>
  <Card title="One-shot operations" icon="dice-five">
    For a payment created from a checkout button press, a random UUIDv4
    generated per attempt is fine. Store it with the attempt so a retry after a
    crash reuses the same key.
  </Card>

  <Card title="Business operations" icon="link">
    For operations tied to a business fact — "refund order 1234 once" — derive
    the key deterministically, e.g. `refund-ord_1234-1`. Then any process,
    host, or job retrying the operation collapses to a single refund.
  </Card>
</CardGroup>

## Retry recipe

1. Generate the key **before** sending the request and persist it alongside
   your order/operation record.
2. On timeout or 5xx: retry with the same key and the exact same body, using
   exponential backoff (for example 1s, 2s, 4s, ...).
3. On `409 idempotency_key_reused`: your retry's body differs from the
   original. Re-send the original body unchanged; if you intend a *different*
   operation, use a *new* key. On `409 idempotency_key_in_use`, wait briefly
   and retry the same body.
4. Never reuse a key for a logically different request — a new payment attempt
   gets a new key.

<Note>
  `merchant_reference` does not deduplicate requests — two creates with the
  same `merchant_reference` and different idempotency keys produce two
  payments. Idempotency is provided only by the `Idempotency-Key` header.
</Note>
