Idempotency
How to retry safely: the Idempotency-Key header on every write, refund keys, and how webhooks and plugin callbacks dedupe.
Networks fail after a request reaches us but before you read the answer. Retrying must never refund twice or create two products.
The Idempotency-Key header
Send Idempotency-Key with any write made with an API key or an OAuth token, or with a
storefront key. The first answer is kept for 24 hours;
sending the same request with the same key again returns that answer, with Idempotent-Replayed: true, and changes
nothing.
curl -X POST https://api.shopfa.st/v1/store/products \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1b7c2e-launch-pack" \
-d '{"name":"Launch Pack","type":"service","priceMinor":2500,"compareAtMinor":null,"description":"","delivers":"","status":"draft","lowStockThreshold":0}'
- Make the key once per intent (a UUID stored with your record), not per HTTP attempt.
- Keys belong to the key or app that sent them (and, on the Storefront API, to the signed-in buyer): another key never sees them.
- The same key with a different method, path or body answers
422 idempotency-key-reused. - While the first request still runs, a retry answers
409 idempotency-key-in-usewithRetry-After: 1. - Answers that never began (401, 403, 429, 503) are not kept: retry them with the same key.
Refunds
Refunds also take an idempotencyKey in the body: 8 to 80 letters, numbers, dashes or underscores, one per refund
attempt. A repeat with the same key is a no-op that answers the same order.
curl -X POST https://api.shopfa.st/v1/store/orders/$ORDER/refunds \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"amountMinor":1200,"reason":"Duplicate purchase","destination":"original","idempotencyKey":"refund-ord123-1"}'
Without a key
PUTandPATCHset state: sending the same body twice leaves the same result.DELETEof something already gone answers404; treat it as done.- A
POSTthat creates (a product, a discount) withoutIdempotency-Keycreates twice if you send it twice.
AI tools connected over MCP get the same protection through each change tool’s idempotencyKey.
Receiving events
- Webhooks keep the same
webhook-idacross retries and replays. Store the ids you processed and skip repeats. - Plugin callbacks apply each
event_ida payment plugin returns once: ShopFast keeps an inbox of processed provider events per plugin, so a provider retrying its notification never pays an order twice. - The sandbox and real gateways follow the same rule with their own event ids.