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

# Idempotency

> Where Idempotency-Key works and how to retry safely

A connection drops mid-request and the client cannot tell whether it arrived. An
unguarded retry creates a second load or a second document. The `Idempotency-Key`
header solves that: the server remembers the key and answers a repeat the same way it
answered the first request, without creating anything again.

## Where it works today

| Endpoint                     | `Idempotency-Key` |
| ---------------------------- | ----------------- |
| `POST /ingest/{source_type}` | **required**      |
| `POST /documents`            | optional          |
| Other writes                 | not supported     |

<Warning>
  `POST /files`, `POST /documents/{id}/_send`, and the other writing endpoints do not
  accept the header. Repeating `_send` or a participant action returns a state error
  rather than creating a duplicate, so creation is the only dangerous retry — see
  "Living without idempotency" below.
</Warning>

## Creating a document

Send `Idempotency-Key` with `POST /documents` and a repeat of the same request
returns **the same** document instead of a second one.

```bash theme={null}
curl -X POST https://app.doodocs.kz/api/developer/v1/documents \
  -H "X-API-Key: $DOODOCS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: hire-2026-02-10-emp-3f2a" \
  -d '{ "title": "Employment order", "type_id": "5c9e…", "template_id": "tpl_7c1a…", "template_version": 3 }'
```

| Situation                              | Response                                           |
| -------------------------------------- | -------------------------------------------------- |
| First request                          | `200`, the document is created                     |
| Repeat of the same request             | `200`, the same document; no second one is created |
| Same key, different body               | `409`, code `DEVELOPER.IDEMPOTENCY_KEY_CONFLICT`   |
| Repeat while the first call is running | `409`, code `DEVELOPER.IDEMPOTENCY_IN_PROGRESS`    |

A key lives 24 hours and belongs to your API key, so two API keys may use the same
string without colliding. The key is claimed **before** the document is created,
which is what stops two simultaneous requests with one key from producing two
documents — the double-submit case.

A repeat is not served from a cache: the server re-reads the document through the
normal path, so permissions are checked on every reply and the data is current.

## Ingest

You choose the key and send it as a header. It must be unique per batch content: the
same batch keeps its key, a new batch gets a new one.

```bash theme={null}
curl -X POST https://app.doodocs.kz/api/developer/v1/ingest/KEDO \
  -H "X-API-Key: $DOODOCS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: employees-2026-02-10-001" \
  -d '{ "payload_type": "EMPLOYEES", "create_missing": true, "records": [] }'
```

| Situation                   | Response                                           |
| --------------------------- | -------------------------------------------------- |
| Header not sent             | `400`, code `INTEGRATION.IDEMPOTENCY_REQUIRED`     |
| First request with this key | `200`, the load is accepted with status `ACCEPTED` |
| Repeat with the same key    | `409`, code `INTEGRATION.IDEMPOTENCY_REQUIRED`     |

The `409` is not a bug in your logic; it confirms the first attempt landed. Treat it
as success and do not resend the batch.

<Tip>
  A good key is deterministic and derived from the data: `employees-2026-02-10-001`,
  `persons-<batch hash>`. A random UUID generated on every attempt protects nothing,
  because the retry carries a new one.
</Tip>

## Living without idempotency

Use `Idempotency-Key` for document creation. For the other writes, lower the risk
like this:

<Steps>
  <Step title="Record your own identifier before the call">
    Store the intent ("create an order for employee X from template Y") together with
    your own operation key, and only then call the API.
  </Step>

  <Step title="After a drop, do not retry blindly">
    First check whether the document already exists:
    `GET /documents?type_id=…&created_after=…&search=…`. If it does, store its `id`
    and skip the retry.
  </Step>

  <Step title="Retry reads and explicitly safe actions only">
    `GET` requests are free to repeat. Repeating `_approve` or `_sign` on a slot that
    already acted returns a state error rather than creating a duplicate.
  </Step>
</Steps>

The document list and its filters are described in
[Listing documents](/en/guides/documents/list).
