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

# Retries and duplicates

> Safe retries on a write, and catching up on webhooks without losing or double-counting one.

<Info>
  **Writes and the change feed described below are live** for inventory (any plan with API access);
  for patients, appointments, clinical notes and drug requests, a write also needs the clinic's API
  add-on and an accepted API data agreement — see [the index page](/index#patient-records-appointments-clinical-notes-and-drug-requests).
  Webhooks are set up from the developer dashboard's **Webhooks** page — see
  [Verifying a webhook's signature](/verifying-webhook-signatures).
</Info>

Two different problems share one shape here: a write that might have been sent twice, and a webhook you might
have missed. Both are solved the same way — a number or a key you keep, and pass back on your next request.

## Idempotency keys — for writes

Every write endpoint (anything that creates, adjusts, or changes a record — receiving stock, adjusting a quantity,
transferring between locations, creating an item, uploading a code list) accepts an `Idempotency-Key` header.

```bash theme={null}
curl -X POST "https://api.clinikehr.com/v1/clinics/YOUR_CLINIC_ID/inventory/adjust" \
  -H "Authorization: Bearer ehr_live_EXAMPLEKEYID0000000000_EXAMPLESECRET…" \
  -H "Idempotency-Key: 5f2c9b1e-7a1a-4b3e-9d2a-2b6f7c8a9d10" \
  -H "Content-Type: application/json" \
  -d '{"item_id":"item_01example","location_id":"loc_01example","lot_id":"lot_01example","delta":-5,"reason":"Cycle count correction"}'
```

Generate a fresh key (a UUID is a good choice) once, when the action is first attempted — never a new one on every
retry. If your request times out, or you're not sure whether it was received, retry with the **same** key:

* If the original write never reached the server, the retry is the first attempt — it goes through normally.
* If the original write already succeeded, the retry returns the **same result** it returned the first time
  (`idempotent_replay: true` in the response) and changes nothing a second time. You will never end up with a
  duplicated stock movement or a doubled adjustment from retrying.

A write endpoint that requires this header refuses a request with none — there is no way to opt out of it on a
route where a duplicate would be a real problem (money, stock).

## The sequence number — for webhooks

Every webhook carries a `seq` — a number that only ever goes up, scoped to the connection that received it.
Store the highest `seq` you've successfully processed. It's your one source of truth for "have I seen this
already", independent of whether the delivery itself arrived once, twice, or out of order under retry.

```json theme={null}
{ "id": "evt_...", "type": "inventory.availability.changed", "seq": 42, "data": { "id": "item_...", "band": "low" } }
```

## The change feed — catching up after downtime

If your endpoint was down, disabled, or you simply missed a delivery, don't try to reconstruct what changed from
memory — page through the change feed instead, starting after the last `seq` you have. The feed is per clinic —
`after_seq` is the query parameter:

```bash theme={null}
curl "https://api.clinikehr.com/v1/clinics/YOUR_CLINIC_ID/events?after_seq=42" \
  -H "Authorization: Bearer ehr_live_EXAMPLEKEYID0000000000_EXAMPLESECRET…"
```

```json theme={null}
{
  "events": [
    { "id": "evt_...", "type": "inventory.availability.changed", "seq": 43, "data": { "id": "item_...", "band": "out" } }
  ],
  "next_after": 43
}
```

Keep paging with `next_after` until it stops advancing. The feed only ever returns event types your key's **current**
scopes cover — if a scope was removed since an event was queued, that event simply will not appear, the same as if
it had never happened for you.

<Warning>
  The change feed has a retention window (30 days, in the current design) — it is a catch-up mechanism for a short
  outage, not a permanent audit log of everything that ever happened.
</Warning>

## See also

* [Verifying a webhook's signature](/verifying-webhook-signatures)
* [Errors](/errors)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.