Idempotency
A network error on a write leaves you not knowing whether it happened. Routes that change many records at once
require an Idempotency-Key so a retry cannot apply the change twice.
Which routes require it
Section titled “Which routes require it”| Route | Required |
|---|---|
POST /apps/catalog/products/bulk |
yes |
POST /apps/media/imports |
yes |
| other app routes | no — the header is ignored |
A required key that is missing or blank → 400. Longer than 255 characters → 400.
How a key is matched
Section titled “How a key is matched”Keys are scoped to your installation — another app can never collide with yours. A request is identified by
its key and a fingerprint of METHOD + path + raw body bytes. The query string is not part of the
fingerprint, and the body is compared byte for byte: re-serialising the same object with a different key order
or whitespace is a different request.
| Situation | Response |
|---|---|
| First use of the key | The request runs normally. |
| Same key, same request, finished | The original status and body, plus Idempotent-Replay: true. Nothing runs again. |
| Same key, same request, still running | 409 with Retry-After: 1. Retry with the same key. |
| Same key, different request | 422. You reused a key — generate a new one. |
| Original response was larger than 256 KB | 409 saying the work was done and cannot be replayed. Do not retry. |
Original request failed with a 5xx |
The key is released; a retry runs the request again. |
Keys are remembered for 24 hours. After that the same key starts a new request.
Choosing keys
Section titled “Choosing keys”Derive the key from what you are doing, not from the attempt: one key per logical operation, reused on every
retry of it. The reference app uses zugo:{runId}:{vehicle}:{chunk} — unique per sync run and per chunk, stable
across retries within the run.