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

# Idempotency

> Network calls fail in the one way that matters: you don't find out whether the request arrived. Retrying might create a second lead. Not retrying might create

Network calls fail in the one way that matters: you don't find out whether the
request arrived. Retrying might create a second lead. Not retrying might create
none.

Send an **`Idempotency-Key`** and retrying is safe.

```http theme={null}
POST /v1/leads
x-api-key: f9_live_...
Idempotency-Key: order-2026-07-21-0001
Content-Type: application/json

{"first_name":"Ada","last_name":"Lovelace","email":"ada@example.com"}
```

Send that again with the same key and body and you get **the original response
back**, with:

```http theme={null}
Idempotent-Replay: true
```

Exactly one lead exists. Branch on that header if you need to know whether the
record was created just now or earlier.

***

## What each case returns

| You send                                 | You get                                          |
| ---------------------------------------- | ------------------------------------------------ |
| A new key                                | The operation runs normally                      |
| Same key, **same** body                  | The original response, `Idempotent-Replay: true` |
| Same key, **different** body             | `422 IDEMPOTENCY_CONFLICT`                       |
| Same key, first call still running       | `409 IDEMPOTENCY_IN_PROGRESS` — retry shortly    |
| No key, on an endpoint that requires one | `428 IDEMPOTENCY_KEY_REQUIRED`                   |

**422 means you have a bug.** The same key with a different body almost always
means a key was reused across two genuinely different operations — and quietly
running the second one is how duplicates happen.

**409 is normal under concurrency** and is not an error in your data. Two identical
requests raced; one won. Retry in a moment and you will get the winner's response.

***

## Choosing a key

* Make it **unique per operation**, not per retry. All retries of one logical
  operation share a key; two different orders never do.
* Something you already have works best: an order id, an invoice number, a UUID
  generated when the user pressed the button.
* 8–255 characters, from `A–Z a–z 0–9 _ . : @ + -`. No spaces or control
  characters — a key differing only by an invisible character is impossible to
  debug.

Keys are scoped to **your company and the specific endpoint**, so `Idempotency-Key: 1`
cannot collide with another customer's, and the same key on `/v1/leads` and
`/v1/activities` describes two separate operations.

Stored responses expire after **24 hours**. After that the same key is a fresh
operation and will run again.

***

## Which endpoints support it

Today: `POST /v1/leads` and `POST /v1/activities`.

The header is **honoured when supplied but not yet required** on these, because the
API is already live and demanding it would reject every existing integration
overnight. New side-effecting endpoints are expected to require it.

**We recommend sending it on every POST.** It costs nothing when there is no retry,
and it is the only thing that makes a retry safe.

***

## What is stored

Only the response body, its status, and a **SHA-256 hash** of your request body.
The request payload itself is never stored — the hash exists purely to detect a key
reused with a different body, and cannot be turned back into your data.

***

## If it cannot be checked

If the idempotency store is unreachable, the request is **refused** with
`503 SERVICE_UNAVAILABLE`, not executed.

This is deliberate, and it is the opposite of how rate limiting behaves (see
[rate-limits.md](/api-reference/rate-limits), which fails *open*). The difference is what
failure costs: a rate limiter that cannot check lets a request through and nothing
is damaged. An idempotency store that cannot check would let a retry through as a
new operation — the duplicate record, or duplicate spend, this whole mechanism
exists to prevent. Refusing is recoverable; a double charge is not.
