Idempotency
Send an Idempotency-Key so a retry cannot repeat a write.
A network can drop a response after the server has done the work. If you send the write again, you could place a second order or take a second payment. An Idempotency-Key makes the retry safe: the work happens once, and the retry gets the first answer back.
Where it applies
| API | Rule |
|---|---|
| Storefront API | Optional on POST /v1/store/orders and POST /v1/store/orders/{id}/pay. Send it on both |
| Merchant API | Required on every write (anything that is not a GET) |
Without the header on the Storefront API, nothing is deduplicated. On the Merchant API a write without one, or with a malformed one, is refused with 422 validation_error and does nothing.
Coming soon on the Storefront API: choosing bank transfer, newsletter sign-up and saved baskets will also take an Idempotency-Key.
The key
Make one key per attempt at one action, and reuse it on every retry of that action. A UUID works.
- 8 to 128 characters.
- Letters, digits,
-and_only.
curl https://api.shopeazy.co/v1/store/orders \
-H "Authorization: Bearer sez_pub_..." \
-H "Idempotency-Key: 5f0c1d7e-3a52-4c0b-9a1e-7d9c2b6e41aa" \
-H "Content-Type: application/json" \
-d '{ "lines": [{ "itemId": "item_456", "quantity": 1 }], "customer": { "name": "Sipho Dlamini" } }'What happens on a retry
| You send | You get |
|---|---|
| The same key and the same request, after the first succeeded | The first 2xx response again, with the header Idempotent-Replay: true. On the Storefront API that includes the order's accessToken |
| The same key, while the first request is still running | 409 in_flight. Wait a moment and retry with the same key |
| The same key with a different method, path or body | 409 idempotency_key_reused. A new request needs a new key |
| The same key, after the first request failed | The request runs again. A failed request is not remembered |
| Any key, when ShopEazy cannot read the key store | 503 idempotency_unavailable. Nothing was processed. Retry with the same key |
"The same request" means the same method, the same path and query, and the same body. For JSON the order of keys and the spacing do not matter. A multipart/form-data body is left out of the comparison, because every client invents a new boundary when it rebuilds a form.
How long and for whom
A key is remembered for 24 hours. It belongs to the credential that sent it:
- On the Storefront API, to the store key. Two different keys can use the same string without clashing.
- On the Merchant API, to the person who signed in. An OAuth connection has its own namespace, so two AI apps connected by the same person can never replay each other's answers.
With the clients
@shopeazy/storefront-client sends a fresh key on orders.create and orders.pay for you. To make your own retry safe, make one key per attempt and pass it each time:
const key = crypto.randomUUID();
const order = await shopeazy.orders.create(input, { idempotencyKey: key });@shopeazy/merchant-client takes idempotencyKey on every write. It never retries on its own, and it throws a TypeError for a malformed key before sending anything.
import { newIdempotencyKey } from '@shopeazy/merchant-client';
// Make the key once, when the person taps, and reuse it on every retry of that tap.
const key = newIdempotencyKey();
await api.updateProduct({ path: { storeId, productId }, body: { title: 'Rooibos tea' }, idempotencyKey: key });A rule of thumb
If you did not get an answer, or you got 409 in_flight, 429 or a 5xx, send the same request again with the same key. If you got any other 4xx, fix the request and use a new key.