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

# Idempotency

> Make POST requests safely retryable with the Idempotency-Key header. The same key returns the cached response instead of creating a duplicate.

The REST API supports idempotent `POST` requests via the `Idempotency-Key` header. Send a unique key with your `POST`; if the request is retried with the same key (within 24 hours), the API returns the **cached response** from the first call instead of executing the operation a second time.

This is the safe way to retry creates after a network failure or timeout — you'll never accidentally double-create the same record.

## When you need it

Any time the cost of accidentally running an operation twice is non-trivial. The classic case is payments — you don't want to charge a customer twice because a network blip caused the client to retry. Other examples:

* Creating an order
* Creating an invoice or receipt
* Sending a notification
* Submitting a form that triggers downstream side effects

For idempotent reads (`GET`) you don't need the header — re-running the same `GET` is already safe. For `PATCH` and `DELETE`, idempotency is built into the verb (the second call has the same effect as the first).

## How it works

1. Generate a unique key on the client. Persist it for the duration of the operation.
2. Send the `POST` with `Idempotency-Key: <your-key>`.
3. The server processes the request normally on the first call.
4. If the same key arrives again within 24 hours, the server returns the cached response from the first call without re-executing the work.

## Example

First request — creates the order:

```bash theme={null}
curl -X POST "https://your-gateway.example.com/gw/api/rest/orders" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-abc-123-attempt-1" \
  -d '{
    "customerId": "cust-001",
    "total": 99.50,
    "status": "pending"
  }'
```

```json theme={null}
{
  "data": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "customerId": "cust-001",
    "total": 99.50,
    "status": "pending"
  }
}
```

Same request retried — returns the cached response, no duplicate record:

```bash theme={null}
curl -X POST "https://your-gateway.example.com/gw/api/rest/orders" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-abc-123-attempt-1" \
  -d '{
    "customerId": "cust-001",
    "total": 99.50,
    "status": "pending"
  }'
```

The body is identical to the first response. The order was created once.

## Behavior at a glance

| Property         | Value                                             |
| ---------------- | ------------------------------------------------- |
| Applies to       | `POST` requests only (excluding `POST /_query`)   |
| Key lifetime     | 24 hours                                          |
| Key scope        | Per project (isolated by `X-Project-ID`)          |
| Cached responses | 2xx and 4xx status codes                          |
| Not cached       | 5xx responses (server errors retry the operation) |

## Choosing keys

The key needs three properties: **unique**, **stable across retries**, and **bound to the operation**.

* **Unique** — across all operations of this kind. UUIDs are the safest default.
* **Stable across retries** — the client must store the key once and reuse it on every retry. A new key on every retry defeats the purpose.
* **Bound to the operation** — different operations need different keys. Don't reuse `payment-2025-04-08` for two different payments on the same day.

A reliable pattern: combine the entity, a user/session id, and a UUID per attempt.

```text theme={null}
order-{userId}-{uuid}
payment-{customerId}-{uuid}
notification-{recipientId}-{uuid}
```

Generate the key on the client **before** the first attempt and persist it (in memory, in local storage, in your job queue) until the operation succeeds.

## Example: safe payment processing

```bash theme={null}
# Generate once, before the first attempt
IDEMPOTENCY_KEY="payment-$(uuidgen)"

curl -X POST "https://your-gateway.example.com/gw/api/rest/payments" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d '{
    "orderId": "order-001",
    "amount": 150.00,
    "method": "credit_card"
  }'
```

If the network drops before you receive the response, retry with the same key:

```bash theme={null}
# Same key = safe retry, no duplicate payment
curl -X POST "https://your-gateway.example.com/gw/api/rest/payments" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d '{
    "orderId": "order-001",
    "amount": 150.00,
    "method": "credit_card"
  }'
```

## Best practices

* **Use unique, descriptive keys.** A UUID is fine; an entity-typed UUID like `payment-<uuid>` is easier to debug in logs.
* **One key per operation.** Don't reuse the same key for distinct operations — even similar ones.
* **Persist the key before the request leaves the client.** If the client crashes mid-request, the persisted key lets a recovery job resume the operation safely.
* **Combine with retries.** On transient errors (5xx, network failures), retry with the same key and exponential backoff.

## FAQ

<AccordionGroup>
  <Accordion title="What if I send the same key with a different body?">
    The cached response from the first call is returned. Idempotency keys assume the client is retrying the same operation, not changing it. To submit a different operation, generate a new key.
  </Accordion>

  <Accordion title="Are 4xx responses cached?">
    Yes. If the first call returned `422 Validation Failed`, retrying with the same key returns the same `422`. This is safe — the client should fix the request and use a new key for the corrected attempt.
  </Accordion>

  <Accordion title="Why aren't 5xx responses cached?">
    A 5xx is a server-side failure — the operation may not have completed. Retries with the same key actually re-run the operation, so the eventual success is captured.
  </Accordion>

  <Accordion title="What's the right key length?">
    No strict limit, but keep it under 256 characters. UUIDs (36 characters) are well within bounds and collision-resistant by construction.
  </Accordion>

  <Accordion title="Does this work on `POST /_query`?">
    No. `_query` is a read-only operation; idempotency doesn't apply. `Idempotency-Key` is ignored on that endpoint.
  </Accordion>
</AccordionGroup>
