Idempotency and replay
Write operations take an Idempotency-Key header of 1 to 255 characters. Draft and tool validation,
campaign recipient checks, POST /calls/search and POST /usage/query do not change a resource and
take no key.
The rules
-
One key per intended action. Generate a new key, for example a UUID, for each action you mean to take. Persist it before you send the request.
-
Same key, same request: the original answer. Sending the same key with the same request again returns the original status code and body, with the response header
Idempotent-Replayed: true. Nothing happens a second time: a replayedcreateCalldoes not place a second call. -
Same key, different request: refused. A changed method, path, body or original
If-Matchunder a used key answers409 idempotency_key_reused. Fix the caller; do not retry a changed intent with the old key. -
Still running: wait. If the first request with the key is still in progress, a duplicate answers
409 idempotency_in_progresswithRetry-Afterat once; it does not wait for the first request. Wait the stated seconds, then resend the identical request with the same key: it then replays the first answer. This holds for every keyed mutation. -
24 hours. Keys are scoped to your workspace and to the operation, and are kept for 24 hours. After that, read the resource to find out what happened before you act again. This is not permanent deduplication.
-
Authorization is checked again. A replay still requires a key that is valid now.
What a replay returns
A replay returns the stored receipt of the first attempt: it describes that attempt, not the resource as it is now. The server looks the key up before it checks the current revision, so a replay of an edit returns its original answer even if the resource has changed since.
No receipt holds a credential value. The rule behind the table: a replay returns the original body, except that a body carrying personal data that can be erased (phone numbers, variables, transcripts) is kept as identifiers only and read again, because erasure must not leave that data in a 24-hour receipt. The answers above are designed not to carry such data, which is why they can be stored whole.
Refusals
A refused request can also be stored, and then replays as the same refusal for the key’s 24 hours. Two cases differ:
createCallrefused with503 at_capacityor429 quota_exceeded: waitRetry-After, then send the identical request with the same key. It is admitted afresh.- The secret operations store nothing when they refuse. After you fix the cause, the same request with the same key runs.
A safe retry
For internal_error, provider_unavailable, unavailable, at_capacity and
call_orchestrator_unavailable, a retry with the same key is safe: if the first attempt took effect,
the stored outcome is replayed; if it had no effect, the retry performs the operation once. Never
retry with a new key.
Keys are not resource keys
An Idempotency-Key protects one request. A resource_key names a piece of configuration across
script runs, and is not an idempotency key. See Resource keys.
Key rotation on the server
If the platform rotates its master key material, idempotency keys used in the previous 24 hours
answer idempotency_key_expired. Read the resource to reconcile, then send a new request with a new
key.