Idempotency-Key on any operation that lists it, and a retry becomes safe.
Three rules
2. Same key, same request → replay
2. Same key, same request → replay
Byte for byte the stored response, including its HTTP status.A retry that produces a different answer is not idempotent even when both
answers are correct.
3. Same key, different request → 409
3. Same key, different request → 409
What counts as “the same request”
A stable hash of the agent, the input and the options — canonical JSON with sorted keys and no whitespace. Two semantically identical bodies that differ only in key order are one request, not two.A replay may answer 200 where the first call answered 201 or 202
The stored status is replayed as it was recorded, so this is expected:Telling a replay from a fresh execution
The safe client check is: look for either header, and treat a 200 on
POST /documents as a replay.
This is a known inconsistency, stated in the generated reference on each
operation rather than smoothed over. The generated document reads the header
out of each handler, so the name on an operation is the name that endpoint
actually sets.
The window
A key stays replayable for 24 hours. Long enough to cover a client’s retry budget and a queue’s redelivery; short enough that the idempotency table is not a second, longer-lived copy of your responses. An unfinished claim may be taken over after 15 minutes. A worker that died mid-run would otherwise leave a record that 409s the retry forever — which is the opposite of what an idempotency key is for.Keys are compartmented, never global
A key is scoped by endpoint, environment and caller identity — never by workspace alone. So two end users of one embedded application, both sendingIdempotency-Key: 1, cannot meet. Nor can the same key on
POST /responses and POST /documents.
The identity part is the end-user principal when a delegated token named one, and
otherwise the credential prefix. Nothing in the compartment would be a secret if
it appeared in a log line.
Where to send it
The header isIdempotency-Key on every endpoint that supports it.
POST /jobs additionally accepts idempotency_key in the body, because a caller
using a generated client that cannot set headers is not a caller who should have
to give up idempotency.
idempotency_key is refused as a runtime option with a 400 telling you to
send the header instead.
Longer-lived deduplication
For conversations there is a better tool than an idempotency key.external_ref is your own id for a thread: resolving the same one twice returns
the same conversation rather than a second one beside it, and it has no 24-hour
window. See Conversations.
Which operations support it
EveryPOST that creates something:
/responses, /conversations, /conversations/{id}/messages,
/agents/{id}/runs, /documents, /extractions, /reports,
/knowledge/ingestions, /jobs.
Each operation in the generated reference lists Idempotency-Key explicitly when
it reads one.
Cancellation endpoints need no key.
POST /responses/{id}/cancel and
POST /jobs/{id}/cancel are idempotent by construction: doing either twice is
indistinguishable from doing it once.
