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

# Idempotency

> Retry mutating KnoxCall API requests safely with an idempotency key — replay semantics, and the 409 / 422 rules that keep retries from creating duplicates.

# Idempotency

Network calls fail in the worst way: the request reaches the server, the response is lost, and
your client has no idea whether the operation happened. Retrying blindly risks creating the
resource twice. An **idempotency key** makes a retry safe — KnoxCall recognises the repeated
key and replays the original outcome instead of performing the operation again.

Send an idempotency key on any **mutating** request (`POST`, `PUT`, `PATCH`, `DELETE`). It is
ignored on safe `GET` requests, which are naturally idempotent.

## Sending an idempotency key

Send a unique key per logical operation. A UUID is a good default.

| Header              | Notes                                                                                         |
| ------------------- | --------------------------------------------------------------------------------------------- |
| `X-Idempotency-Key` | The canonical KnoxCall spelling.                                                              |
| `Idempotency-Key`   | The standard spelling is **also accepted** — use whichever your HTTP client makes convenient. |

```bash theme={"dark"}
# Mint a 1-hour OAuth token (client_credentials) — see /api-reference/authentication
TOKEN=$(curl -s -X POST https://api.knoxcall.com/oauth/token \
  -u "$KNOXCALL_CLIENT_ID:$KNOXCALL_CLIENT_SECRET" \
  -d "grant_type=client_credentials" | jq -r .access_token)

curl -X POST https://api.knoxcall.com/v1/secrets \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: 3f8a9b2c-1d4e-4f6a-8b0c-2e1f0a3b4c5d" \
  -d '{ "name": "STRIPE_API_KEY", "value": "sk_live_..." }'
```

<Note>
  Generate a **fresh** key for each distinct operation, and reuse the **same** key only when
  retrying that exact operation. Reusing one key for two genuinely different requests is an
  error — see [Reuse with a different body](#reuse-with-a-different-body-422).
</Note>

## Replaying a stored response

The first request with a given key is processed normally and its response is stored. Any later
request with the **same key and the same body** does not re-run the operation — KnoxCall
replays the stored status code and body, and adds a header so you can tell it was a replay:

```http theme={"dark"}
HTTP/1.1 201 Created
X-Idempotent-Replay: true
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
  "data": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "STRIPE_API_KEY" },
  "meta": { "request_id": "550e8400-e29b-41d4-a716-446655440000" }
}
```

<Tip>
  `X-Idempotent-Replay: true` means "you are seeing a stored result, not a fresh one." Its
  absence means the request executed for real. The replayed body is byte-for-byte the original
  response, including its original status code.
</Tip>

## Error semantics

Idempotency errors use the canonical [error envelope](/api-reference/errors).

### Still in progress → `409`

If a request with the same key is **still being processed** when a retry arrives, the retry is
rejected with `409` and the `request_in_progress` type. Wait briefly and retry — the original
is on its way to completion.

```json theme={"dark"}
{
  "error": {
    "type": "request_in_progress",
    "message": "A request with this idempotency key is still being processed.",
    "request_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}
```

### Reuse with a different body → `422`

If you reuse a key that was already used for a **different** request body, KnoxCall rejects the
new request with `422` and the `idempotency_key_reuse` type rather than silently doing the
wrong thing. This is the API's only `422`. The fix is to use a fresh key for the new operation.

```json theme={"dark"}
{
  "error": {
    "type": "idempotency_key_reuse",
    "message": "This idempotency key was already used with a different request body.",
    "request_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}
```

### Malformed key → `400`

A key that is malformed or too long is rejected with `400` and the `invalid_idempotency_key`
type before any work is done.

### Summary

| Condition                                   | Status        | Error type                | What to do                                                     |
| ------------------------------------------- | ------------- | ------------------------- | -------------------------------------------------------------- |
| Same key, same body, original finished      | 200 / 201 / … | —                         | You get the replayed response with `X-Idempotent-Replay: true` |
| Same key, same body, original still running | 409           | `request_in_progress`     | Wait briefly and retry                                         |
| Same key, **different** body                | 422           | `idempotency_key_reuse`   | Use a fresh key for the new operation                          |
| Malformed / oversized key                   | 400           | `invalid_idempotency_key` | Send a well-formed key (a UUID works)                          |

## Recommended pattern

1. Generate one key per logical operation (a UUID) and hold onto it for the duration of your
   retries.
2. Send it as `X-Idempotency-Key` on the mutating request.
3. On a network error, timeout, or `5xx`, **retry with the same key** — you will either
   perform the operation once or replay its stored result.
4. On `409 request_in_progress`, wait briefly and retry the same key.
5. Never reuse a key for a different operation; if the body must change, mint a new key.

<Tip>
  The first-party [SDKs](/sdks/overview) attach an idempotency key to mutating requests
  automatically and reuse it across their internal retries, so safe retries work out of the box.
</Tip>

## What's Next?

<CardGroup cols={2}>
  <Card title="Errors" icon="triangle-alert" href="/api-reference/errors">
    The canonical error envelope and the full type → status table.
  </Card>

  <Card title="Rate limits" icon="gauge" href="/api-reference/rate-limits">
    Headers, `Retry-After`, and backoff guidance for `429` responses.
  </Card>

  <Card title="API Overview" icon="book" href="/api-reference/overview">
    Base URLs, response envelope, pagination, and quick examples.
  </Card>

  <Card title="Authentication" icon="lock" href="/api-reference/authentication">
    OAuth 2.1 + DPoP, CLI login, and API key types.
  </Card>
</CardGroup>
