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

# Idempotency

<Warning>
  The event ingestion endpoint (`POST /events`) does **not** support the
  `Idempotency-Key` header. Instead, each event carries a client-defined
  `unique_id` field which is enforced as a natural unique key — sending the same
  `unique_id` again is silently deduplicated. Use `unique_id` to make event
  ingestion safe to retry; do not rely on `Idempotency-Key` for that endpoint.
</Warning>

All mutating API endpoints (POST, PATCH, DELETE) support idempotency keys
to safely handle retries without duplicating side effects.

## How it works

Include an `Idempotency-Key` header with a unique string value in your request.
If you send the same request with the same idempotency key within 24 hours,
the API returns the original response without re-executing the operation.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.alguna.io/invoices \
    -H "Authorization: Bearer <API-KEY>" \
    -H "Alguna-Version: 2026-04-01" \
    -H "Idempotency-Key: ik_create_invoice_cust123_20260330" \
    -d '{ "customer_id": "cust_abc123" }'
  ```

  ```typescript TypeScript theme={null}
  const invoice = await alguna.invoices.create({
    customer_id: "cust_abc123",
  }, {
    idempotencyKey: "ik_create_invoice_cust123_20260330"
  });
  ```
</CodeGroup>

## Key behaviors

| Scenario                                | Response                                                |
| --------------------------------------- | ------------------------------------------------------- |
| First request with key                  | Operation executes normally                             |
| Replay with same key + same params      | Cached response returned (no side effects)              |
| Replay with same key + different params | `422` error: key already used with different parameters |
| Concurrent request with same key        | `409` error: request currently being processed          |
| Request after key expires (24h)         | Operation executes as new                               |

## Response headers

Every response to a request that includes an idempotency key will include:

| Header                | Description                                                                |
| --------------------- | -------------------------------------------------------------------------- |
| `Idempotency-Key`     | Echo of the key you provided                                               |
| `Idempotent-Replayed` | `true` if this response was served from cache, `false` if freshly executed |

When a request fails with a transient error (5xx or 429), the response includes:

| Header            | Description                                                                               |
| ----------------- | ----------------------------------------------------------------------------------------- |
| `Transient-Error` | `true` — the idempotency key has been released and you can safely retry with the same key |

## Error handling

Idempotency keys interact with errors as follows:

* **2xx responses**: Cached and replayed on retry
* **4xx responses** (validation, not found, etc.): Cached and replayed — these represent deterministic outcomes
* **429 Too Many Requests**: **Not cached** — the key is released so you can retry after the rate limit resets
* **5xx responses**: **Not cached** — the key is released so you can retry the operation

## Best practices

* **Generate unique keys**: Use UUIDs, ULIDs, or a combination of meaningful identifiers
  (e.g., `ik_create_invoice_{customer_id}_{timestamp}`)
* **Scope keys to operations**: Don't reuse the same key across different endpoints
* **Retry with the same key**: If your request times out or you receive a 5xx error,
  retry with the same idempotency key — the server guarantees at-most-once execution
* **Don't retry on 4xx**: Client errors (400, 422, etc.) are deterministic and will
  return the same error on replay

## Key format

* Maximum 255 characters
* Must contain only printable ASCII characters
* We recommend prefixing with a short identifier: `ik_<your_unique_value>`

## Expiry

Idempotency keys expire after **24 hours**. After expiry, the same key string can be
reused for a new operation.

## Which endpoints support idempotency?

All mutating endpoints (POST, PATCH, DELETE) accept the `Idempotency-Key` header.
GET requests are naturally idempotent and do not support the header.

The event ingestion endpoint (`POST /events`) is the one exception — it uses
the per-event `unique_id` field as a natural unique key instead of the
`Idempotency-Key` header.
