Idempotency Keys
This document specifies how idempotency works on the API: the Idempotency-Key header, the guarantee it gives, how long a key is remembered, and the errors that arise from key misuse. The purpose is safe retries: networks fail and clients time out, and a retried write must have the effect of the write sent once.
The header
Every write endpoint (POST, PATCH, PUT, DELETE) accepts the header, and writes that create a resource or move value: a shipment, a refund, a stock adjustment, a price change: shall carry one
Idempotency-Key: ship-ord_64kx20-1
- A key is a string of 8 to 128 characters: letters, digits, hyphens, and underscores. Generate one per logical operation: a value unique to that operation in your systems, such as the operation's own identifier, works well.
- Generate the key when the operation is decided, and reuse the same key on every retry of that operation. Generating a fresh key per attempt defeats the mechanism entirely.
- A key is scoped to your account and to the endpoint it was sent to. The same key sent to a different endpoint is a different key.
The guarantee
- First request. The Platform executes the write and records the key with the request and its result.
- Retry with the same key and the same body. The Platform does not execute the write again. It returns the recorded result: the same status code and the same response body, including the created resource's
id. - Retry with the same key but a different body. The request fails with
409and codeidempotency_conflict: the key names one operation, and a different body is a different operation that needs its own key. - Concurrent duplicates. If two requests with the same key arrive together, one executes and the other returns its result when ready, or
409with coderequest_in_progressif the first is still running after a short wait. Retry that response with the same key.
Keys are remembered for 24 hours. A key reused after that window is treated as new and executes again: which is why a key should name the operation, not the attempt: a genuinely new operation the next day deserves a new key, and an old key recycled by accident is visible in your own logs.
Which operations rely on it
| Operation | Why the key matters |
|---|---|
| Create product or variant (Catalog Endpoints) | A duplicate listing is hard to unwind once orders reference it |
| Stock adjustment (Inventory Endpoints) | A doubled adjustment silently corrupts stock |
| Order acknowledge or cancel (Orders Endpoints) | A repeated cancel must not refund twice |
| Create shipment (Fulfillment Endpoints) | A duplicate shipment confuses carrier, buyer, and stock |
| Refund on a return (Returns Endpoints) | Money must move once |
| Price set or schedule (Pricing Endpoints) | A replayed stale price must not overwrite a newer one |
Reads (GET) neither need nor use a key; the header is ignored on reads.
Receiving webhooks is the mirror problem
Webhook deliveries can repeat for the same reason requests can: a timeout looks identical to a failure. The Platform retries deliveries (Webhook Event Schemas); on your side, record each event id handled and skip repeats, so a redelivered order.created creates one internal order, not two (Payments Endpoints reconciliations depend on the same discipline).
Errors
| Status | Code | Cause |
|---|---|---|
| 409 | idempotency_conflict | The key was already used with a different request body |
| 409 | request_in_progress | The original request with this key is still executing; retry shortly with the same key |
| 422 | invalid_idempotency_key | The key is too short, too long, or carries disallowed characters |
Error bodies follow Error Codes.
Rules
- One operation, one key, for the life of the operation: across retries, restarts, and deploys of your client.
- Never reuse a key for a different operation, even after the 24-hour window; treat keys as single-use names.
- Store the key with the operation in your own records, so a crash between sending and recording the result can be resolved by retrying with the same key and reading the recorded result.
- A
409 idempotency_conflictis a client bug: stop, compare the bodies, and assign the new operation its own key.