Core conceptsIdempotency

Idempotency

Send an Idempotency-Key header on mutations so a network retry cannot execute the same operation twice. Use a fresh UUID v4 per logical operation — not per retry.

Where it is required

These high-risk logistics and money routes reject a request without the header with 400 IDEMPOTENCY_KEY_REQUIRED:

POST /orders/{id}/shipPOST /orders/bulk-ship
POST /orders/{id}/shipmentPOST /orders/bulk-cancel
POST /orders/{id}/cancelPOST /fulfillments
POST /returns/{id}/approve-refund

On the bulk endpoints — POST /products/bulk and POST /inventory/bulk-update — the header is optional but strongly recommended, in both their synchronous and asynchronous (job) modes: a key lets a retried submission replay the original result instead of creating a duplicate batch. See Bulk operations & jobs.

On every other mutation the header is optional but strongly recommended. When it is absent the response carries X-MallPlus-Idempotency: absent so you can detect unprotected calls in your own logs.

Semantics

SituationResult
Same key, same body, after completionThe original response is replayed. The operation does not run again
Same key, different body422 IDEMPOTENCY_KEY_REUSED — the key is bound to the body it first saw
Same key, first request still running409 IDEMPOTENCY_KEY_IN_PROGRESS with Retry-After
Original returned 5xx or threwThe reservation is released; retrying with the same key re-executes

Treat idempotency as strong protection, not a mathematical guarantee. Replay protection is scoped to a short processing window, so a very long-running synchronous request is not covered indefinitely. For large batches use the async path and reconcile against the job result rather than assuming exactly-once delivery.