---
title: "Idempotency"
description: "Retry a write safely with an Idempotency-Key."
url: https://developer.tendralhealth.com/docs/idempotency
---

# Idempotency

Send an `Idempotency-Key` header on a request that changes something, and a retry after a network failure cannot do it twice. No public endpoint takes a `POST` yet; this is how every one will behave.

## How it works

- The first request with a key runs, and its status, headers and body are kept for 24 hours under your API key and that idempotency key.
- A retry with the same key and the same body returns the stored response with `Idempotent-Replayed: true`. Nothing runs twice.
- A retry with the same key and a different body gets `409` with code [`IDEMPOTENCY_KEY_REUSED`](/docs/errors#IDEMPOTENCY_KEY_REUSED).
- Only successful (`2xx`) responses are kept. A failed request can be retried with the same key.

## Pattern

Make one key per operation, before the first attempt, and reuse it for every retry of that operation.

```typescript Node
import { randomUUID } from 'node:crypto'

async function postOnce(url: string, body: unknown): Promise<unknown> {
  const key = randomUUID()
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(url, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.TENDRAL_API_KEY}`,
        'Idempotency-Key': key,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body),
    })
    if (res.ok) return res.json()
    if (res.status < 500 && res.status !== 429) throw new Error(await res.text())
    await new Promise((r) => setTimeout(r, 2 ** attempt * 1000))
  }
  throw new Error('Gave up after 5 tries')
}
```
