How it works
Send anIdempotency-Key header on any POST, PUT, PATCH, or DELETE. Use a UUID v4 per logical operation — the same value for every attempt of that operation, a new value for the next one.
The stored result is keyed on (your user, the key, the route). The same key on a different route is a different entry, so reusing a key across two distinct endpoints does not collide. Reusing a key for a genuinely different request body on the same route returns the original response rather than performing the new one — the key, not the body, is authoritative.
What gets stored, and for how long
- Results are retained 24 hours.
- Only
2xxresponses are stored. A failed request does not burn the key — retrying after an error genuinely re-executes, rather than replaying the failure. - A replay returns the original status and body, plus the
X-Idempotent-Replayheader. Check that header to tell a replay apart from a fresh execution — see Response headers. - Idempotency does not apply to
GET. Those are already safe to repeat and ignore the header if sent.
Worked example: retry after a timeout
The client sends a request, the connection drops before the response arrives, and the client retries with the same key.X-Idempotent-Replay present means the second call did no new work and was not billed again — it returned attempt 1’s stored result.
Common mistakes
Generating a new key on every retry
Generating a new key on every retry
This defeats the purpose entirely — each retry looks like a brand-new operation and can duplicate work or double-charge. Generate the key once per logical operation, before the first attempt, and reuse it for every retry of that same attempt.
Reusing a key for a different request body
Reusing a key for a different request body
The key, not the body, is authoritative. Reusing
6c9b1e2a-... for an unrelated request on the same route returns the original stored response, not the result of the new body. Mint a fresh key per distinct operation.Assuming a failed attempt burned the key
Assuming a failed attempt burned the key
Only
2xx responses are stored. If attempt 1 returned a 4xx or 5xx, the key is still free — retrying with the same key genuinely re-executes the request rather than replaying the failure.Sending it on GET
Sending it on GET
GET requests are already safe to repeat. The header is accepted but has no effect there.Expecting it to work past 24 hours
Expecting it to work past 24 hours
Stored results expire after 24 hours. A retry sent after that window with the same key executes as a new request rather than replaying.
The Auto Apply exception
POST /api/auto-apply/applications is designed not to use the shared 24-hour cache. It is a contract preview and currently returns 503 auto_apply_coming_soon for every request regardless of key — see Errors. At launch it will track its own durable idempotency record and return dedicated conflict codes instead of a silent replay:

