Idempotency

Idempotency #

HTTP clients and agent frameworks retry on timeout by default. A retried POST /transaction/{id}/post must not post the same entry twice to a ledger that can only be corrected with a contra document. Send an Idempotency-Key header with any write, and the API answers a retry with the original response instead of doing the work again.

curl -X POST https://api.microbooks.io/books/v1/transaction/42/post \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: 6b0f6c1e-3f6c-4b8e-9a1d-7d2f0f3a9c11"

Generate a new key, such as a UUID, for every distinct operation, and reuse it only when retrying that same operation.

Behaviour #

SituationResult
First request with a keyRuns normally; a successful response is stored
Retry with the same key and the same bodyThe stored status and body are returned unchanged, with Idempotent-Replayed: true. Nothing is done again
Same key, different body422 with errors.idempotency_key. Use a new key for a new request
The first request failed (4xx/5xx)Nothing is stored, so a corrected retry under the same key runs normally
Dry run (rollback header)Never stored or replayed
Concurrent duplicatesThe second waits for the first to finish, then receives its response
  • Scope. A key is scoped to your user, the HTTP method and the path. The same key on two different routes is two independent requests.
  • Lifetime. Keys are kept for 24 hours (conventions.idempotency_ttl_hours in /meta). A retry after that runs as a new request.
  • Format. 1 to 255 visible ASCII characters. Anything else is a 422.
  • Reads. GET requests ignore the header.

A retry recipe #

  1. Create a key before the first attempt and keep it with the operation.
  2. On a timeout, a connection error or a 5xx, retry with the same key and the same body.
  3. On Idempotent-Replayed: true, treat the response as the outcome of the first attempt.
  4. On a 4xx, fix the request. You may keep the key, since failed attempts are not stored.