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

# Idempotency

> Retry any write safely, without creating something twice

## Writes that change an existing resource

No header is involved. Where a repeat used to return an error, it now returns the current state:

| Endpoint                                                          | A repeat call returns                          |
| ----------------------------------------------------------------- | ---------------------------------------------- |
| [Cancel Appointment](/api/appointments/cancel-appointment)        | `200` with the cancelled appointment           |
| [Cancel Shipment](/api/shipments/cancel-shipment)                 | `200` with the cancelled shipment              |
| [Cancel Home Kit Order](/api/home-kit-orders/cancel-home-kit)     | `200` with the cancelled order                 |
| [Activate Home Kit Order](/api/home-kit-orders/activate-home-kit) | `200`, if the collection time is the same one  |
| Removing panels or biomarkers                                     | `200`, whether or not they were still attached |

Two exceptions return `409` instead:

* **Activating with a *different* `activated_at`.** The collection time is what the lab report carries, so a retry must not silently overwrite it.
* **Cancelling after the point of no return** — a confirmed blood draw, a collected shipment, an activated kit.

[Confirm Blood Draw](/api/appointments/confirm-blood-draw) is the one endpoint deliberately not retry-safe: a second call returns `409`.

## Writes that create something

Four endpoints accept an optional `Idempotency-Key` header, at most 255 characters:

* [Create Profile](/api/profiles/create-profile)
* [Create Appointment](/api/appointments/create-appointment)
* [Create Home Kit Order](/api/home-kit-orders/create-home-kit)
* [Create Shipment](/api/shipments/create-shipment)

Keys are scoped to your API key **and** to the endpoint, so they never collide with another partner's or with the same key used elsewhere.

* **Same key, same body** → for 24 hours you get the original response back, marked `Idempotent-Replayed: true`. Field order and whitespace don't matter.
* **Same key, different body** → `409`.
* **An empty `Idempotency-Key`** → `400`, rather than being ignored.

### When the key is released

Creation is **not atomic** — a `5xx` does not mean nothing was written.

* **The request failed before anything was created** → the key is released, so you can retry under it.
* **The request failed after something was created**, or **never returned at all** → the key stays claimed and retries get `409`. Fetch the resource to check whether it exists, then use a fresh key if it doesn't.

<Note>
  A replay returns the response body recorded when the resource was created — a snapshot, not a live
  read. If the resource has changed since, fetch it directly.
</Note>

### Some creates also have a natural key

Two endpoints reject duplicates on their own, independently of any header:

* [Create Appointment](/api/appointments/create-appointment) allows one appointment per profile, location and instant. A repeat returns `409` carrying the existing `appointment_id`.
* [Create Profile](/api/profiles/create-profile) returns `409` when `email` already has a profile. Omit `email` and a unique placeholder is generated per request, so repeats *do* create separate profiles.

When both apply, the natural-key `409` is what you see, and only it carries the extra field (`appointment_id`, or an `errorName` discriminator) — an idempotency `409` carries `error` alone, so read those fields defensively.
