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

# Idempotent requests

> Send an Idempotency-Key header so a retried write cannot be applied twice.

## Overview

Network timeouts leave you guessing: did the upload land, or not? Retrying is
safe only if the API can recognise the retry. Every write endpoint accepts an
optional `Idempotency-Key` request header for exactly that.

The first request with a given key does the work and its response is stored for
**24 hours**. A retry with the same key **and the same body** replays that
stored response instead of doing the work again, and carries the header
`Idempotent-Replayed: true`.

<Info>
  The header is optional everywhere. Existing integrations keep working
  unchanged — omit it and requests behave exactly as before.
</Info>

## Using it

<Note>
  Examples use the default base URL, `https://api.orion.file.ai/prod/v1`. If
  your workspace is on an instance-specific host, swap the hostname and change
  nothing else — see [Switching between
  instances](/docs-api/api-intro#switching-between-instances).
</Note>

<Steps>
  <Step title="Generate a key per logical operation">
    Any opaque string up to 255 characters. A UUIDv4 is recommended. Reuse the
    *same* key for every retry of the same operation, and a fresh key for a new
    operation.
  </Step>

  <Step title="Send it as a request header">
    ```bash theme={null}
    curl -X POST "https://api.orion.file.ai/prod/v1/directories" \
      -H "x-api-key: YOUR_API_KEY" \
      -H "Idempotency-Key: 0f1c8e03-978e-40d5-bc93-6894a57f9324" \
      -H "Content-Type: application/json" \
      -d '{"name": "Q3 Invoices"}'
    ```
  </Step>

  <Step title="Retry with the same key on failure">
    On a timeout, `429`, or `5xx`, retry with the identical key and body. If the
    original request had already succeeded, you get its response back with
    `Idempotent-Replayed: true` and nothing is created twice.
  </Step>
</Steps>

## Outcomes

| Situation                                       | Result                                                                   |
| ----------------------------------------------- | ------------------------------------------------------------------------ |
| First request with this key                     | Executes normally. Response stored for 24h.                              |
| Same key, same body, original finished          | `200`-series replay of the stored response, `Idempotent-Replayed: true`. |
| Same key, same body, original **still running** | `409` `IDEMPOTENT_REQUEST_IN_PROGRESS`.                                  |
| Same key, **different** body                    | `422` `IDEMPOTENCY_KEY_REUSED`.                                          |
| Key older than 24h                              | Treated as a new key — the request executes again.                       |
| No `Idempotency-Key` header                     | No idempotency handling at all.                                          |

<Note>
  "Same body" is determined by a fingerprint over the method, path, workspace,
  and a canonical form of the request body — key ordering and whitespace do not
  matter, so you can resend a re-serialized body safely.
</Note>

### Cached failures

A terminal failure is cached and replayed too — a `403`, `404`, or business
`409`/`422` will not be re-executed under the same key.

Two classes are deliberately **not** cached, so a retry gets a fresh attempt:

* Transient statuses (`408`, `425`, `429`) — a later retry may legitimately succeed.
* `VALIDATION_FAILED` and `BAD_REQUEST` — a corrected body has a different
  fingerprint and must not be locked out for 24 hours.

## Endpoints

`Idempotency-Key` is honoured on every write endpoint:

<CardGroup cols={2}>
  <Card title="Files" icon="file">
    `POST /files/upload` · `POST /files/upload/multipart` ·
    `POST /files/upload/multipart/complete` · `DELETE /files` ·
    `PATCH /files/{fileId}`
  </Card>

  <Card title="Schemas & values" icon="table">
    `PATCH /files/schema` · `PATCH /files/{fileId}/values` ·
    `PATCH /files/schema/rerun` · `PATCH /schemas` ·
    `POST /files/schema/export` · `POST /files/schema/import`
  </Card>

  <Card title="File types" icon="tags">
    `PATCH /file-type/approve` · `PATCH /file-type/rename`
  </Card>

  <Card title="Workspace" icon="gear">
    `POST /directories` · `PATCH /project/setting`
  </Card>
</CardGroup>

### Accepted but ignored

Three `POST` endpoints are semantically reads. They accept the header for client
uniformity but never replay: every call executes fresh, with no 24h window, no
`Idempotent-Replayed` header, and no key-reuse `422`.

| Endpoint                    | Why                                                                        |
| --------------------------- | -------------------------------------------------------------------------- |
| `POST /query/execute`       | No side effects, and a later run can legitimately return different rows.   |
| `POST /files/preview`       | Presigned URLs expire in an hour — a 24h replay would hand back dead URLs. |
| `POST /files/preview/batch` | Same as above.                                                             |

<Warning>
  Idempotency fails **open**. If the backing store is briefly unreachable the
  request still proceeds, but non-idempotently — it is a resilience aid, not a
  transactional guarantee. Keep your own retry bookkeeping for operations where
  a duplicate would be costly.
</Warning>
