Idempotency
A network can drop the answer to a request that worked. To make a retry safe, every write carries an X-Idempotency-Key header, and a repeat of the same key gets the first answer back instead of running again.
The rules
Section titled “The rules”- Required on every write.
POST /shipment-requests,PATCH /shipment-requests/{id},POST /shipment-requests/{id}/cancelandPOST /deals/{id}/documentswithout the header (or with one longer than 128 characters) answer400withIntegration.IdempotencyKeyRequired. - 1 to 128 characters, chosen by you. Derive it from your own record, for example
<record-id>-create, so a retry after a restart sends the same key. - Remembered for 24 hours, per key (the API key that made the call), method and path. A repeat within that time is not run again: the first answer comes back with the same status, body and
ETag, and anX-Idempotency-Replayed: trueheader. - The request is compared. A key belongs to one request. The same key with a different request on the same route answers
422withIntegration.IdempotencyKeyReused, and nothing runs. “The same request” means the same JSON for a create, an update or a cancel: spacing, the order and the case of property names, the spelling of a number (5000,5000.0and5e3are one number) and the order of metadata keys do not matter; a value, including a metadata key’s case, does. For an upload it means the same file (by its content), the same category (in any case), the same file name and the same file type (parameters such ascharsetaside); the multipart boundary does not matter. Use a new key for a new request. - Only settled answers are kept. Successes and client errors (
4xx) are replayed; a5xxis not kept, so a retry after a server error runs again for real. - Not a verdict about the moment. A
412(the request changed since you read it) and a429(an allowance ran out) are never kept: retry with the same key, after a fresh read or after the wait, and the retry really runs. Neither is an answer refused before the API read your request (a body that is not JSON or is too large, a missing scope): fix it and retry with the same key. - At the same moment. If a repeat arrives while the first call is still running, it answers
409withRetry-After: 1. Retry after a second with the same key and you get the first call’s answer. - A daily allowance of new keys. Each API key can use 5,000 new idempotency keys per UTC day. Past it, a write answers
429withIdempotency.DailyLimitand aRetry-Afterthat runs to midnight UTC, and nothing runs: retry it after that, with the same key. A repeat of a key already answered that day is not counted and is replayed as usual. - An answer too large to keep whole. An answer over 256 KB, or one given after the key has used its daily storage for answers (8 MB, about 2,000 ordinary answers), is kept by its status only. A repeat still does not run again: it gets that status with
X-Idempotency-Replayed: trueandX-Idempotency-Body-Omitted: true, and no body. Read the record for the rest, for exampleGET /shipment-requests?externalReference=...after a create orGET /deals/{id}/documentsafter an upload.
HTTP/1.1 409 ConflictRetry-After: 1Content-Type: application/json
{ "status": 409, "title": "Conflict", "detail": "The same request is already being processed." }Use one key per intended change: for an update, derive it from your record and the change (for example <record-id>-update-<n>), so the next change gets a new key. Other POST routes (the sandbox advance, the token endpoint) accept the header but do not require it.
Idempotency and externalReference together
Section titled “Idempotency and externalReference together”The two protect against different things:
X-Idempotency-Key |
externalReference |
|
|---|---|---|
| Protects against | The same HTTP call sent twice | The same business record sent twice |
| Lasts | 24 hours | As long as the request exists |
| A repeat gets | The first answer, replayed (X-Idempotency-Replayed: true) |
409 with the first request’s requestId |
So the safe retry after any failure is: the same X-Idempotency-Key and the same externalReference. If the first call created the request, you get its answer back (or a 409 naming it); no second request is ever opened. See externalReference.
What is logged
Section titled “What is logged”The call log keeps a hash of the idempotency key with each call, never the key itself, so your Owner or Admin can match a retry to the call it repeated.
