RaabtaHQ
Idempotency

Idempotency

Retry a POST safely with an Idempotency-Key: the 24-hour replay window, the replay header, and what happens when a key is reused with a different body.

The replay window

A stored result is replayed for 24 hours. A replay is marked so you can tell it from a fresh execution:

Replayed response headers
HTTP/1.1 201 Created
Idempotent-Replayed: true
X-Request-Id: req_01J9X4T7C2XK8M1Q

The X-Request-Id on a replay is a new id — the original and the replay stay distinguishable in logs even though the bodies are identical.

Same key, different request

The stored key is bound to a hash of the endpoint and the raw request body. Reusing a key with a different body, or on a different endpoint, is a client bug rather than a resource conflict:

ResponseWhen
422 unprocessable
reason: idempotency_key_reused
The key was seen before with a different body or on a different endpoint. Nothing is executed; generate a new key for a new request.
409 conflict
reason: idempotency_in_progress
The first request with this key is still running. The response carries Retry-After: 1; retry and you will get the stored result once it completes.

Bytes, not JSON

The hash is over the body exactly as sent. Re-serialising the same object with different key order or whitespace is a different body. Retry the same bytes.

Abandoned requests

A request that claimed a key but never finished — the connection dropped mid-flight — is trusted for 60 seconds. After that the key is reclaimed and the next request with it executes for real. So a 409 here is always short-lived.

What is never stored

  • A 5xx or a 429. Storing a transient failure would make every retry replay the failure forever; instead the key is released and the retry runs the operation.
  • A request without the header. The header is opt-in per request; omit it and the request is simply executed.
  • A 4xx other than 429 is stored: a validation error for the same bytes will be a validation error again, so replaying it is correct and cheap.

See the rate-limits guide for the retry timing on a 429.

Endpoints that honour it

Every endpoint that creates something or has a side effect worth protecting. GETs are naturally idempotent and ignore the header; PATCH and DELETE are idempotent by definition.