Skip to main content
The API is metered — a retry after a network timeout must not double-charge your wallet or create the same resource twice. Send an idempotency key on writes and a retry becomes safe by construction, rather than something you have to reason about at the call site.

How it works

Send an Idempotency-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 2xx responses 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-Replay header. 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

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.
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.
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.
GET requests are already safe to repeat. The header is accepted but has no effect there.
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:
Every other write endpoint follows the shared 24-hour cache described above. Auto Apply application creation is the one exception, and its own idempotency record is not live yet.