# Idempotent writes

A client that retries a write after a timeout can't tell whether the first
try worked. So every route but a `GET` honours the `Idempotency-Key`
header: the client sends the same key with each try, and a repeat gets the
first try's answer. The runtime handles the key entirely; the action never
sees it.

HTTP already calls `PUT` and `DELETE` idempotent, but a retried `DELETE`
answers 404 where the first answered 204, and the action behind any write
may raise events or call providers. So the key covers them too.

- Before the action runs, the runtime claims the key, scoped to the
  caller.
- Afterwards, it keeps the response with a digest (a hash) of the request,
  in the action's transaction, for 24 hours.
- A repeat gets the kept response.
- The same key with a different request fails `idempotency_key_reused`
  (422). A repeat while the first is still running fails `conflict` (409).
- Successes and failures the action returns are kept. `unavailable` and
  internal failures are not, so a retry runs again.
- Calls to outside services in the action send keys derived from the
  client's key ([Calling](calling.md)).

An API can require the key with `idempotency: Idempotency.required`, and
OpenAPI lists the header. When a repeat counts as the same request is under
[How failures reach the client](how-failures-reach-the-client.md). The rest
is in the [design notes](https://github.com/jadpo-dev/jadpo/blob/main/docs/language/notes/07-http.md), "Idempotency keys".
