One idempotency key per order: a retried holiday batch never doubles

Sume Format runs replay safely: same key and body returns the original run with no second charge. Build the key from order id plus a version, never the clock.

4 min readSume
All posts

Send Idempotency-Key on every Sume Format run and derive it from the thing the run makes: your order id plus a version you bump only when you want a re-run. With the same key and the same body, Sume returns 200 with the original receipt and idempotency_hit: true, starts no second run and makes no second charge. During Black Friday week, when webhooks time out and workers restart, that is the line between one video per order and two.

The docs say it plainly: do not derive the key from the time of the request, and a fresh uuidgen per request makes the header useless. The scope of a key is one Format, and a key can be up to 255 characters. See Calling a Format.

What each replay returns

The behaviour depends on what changed between your two requests. Know the four cases before you write your retry loop.

Idempotency replays on a Format run (read 2026-10-04, from the docs)
ReplayResult
Same key, same body200, original receipt, idempotency_hit true, no new charge
Same key, different body409 idempotency_conflict, nothing runs
Same key, two requests at onceOne wins; the other gets 409 idempotency_key_in_use, retryable
Same key after a failed create (402, 503)Key released; fix the cause and retry

A key scheme for an order feed

Use order-<id>-v<n>. Start at v1. If the customer changes the product and you must re-render, bump to v2; the new key produces a new run. If a worker crashes after submit and retries, it sends v1 again and gets the original run back. The key plus the body decide the result, so changing even the instruction under the same key is a conflict, and you should treat that as a bug in your retry code, not something to work around.

curl -sS -X POST "https://api.sume.com/v1/formats/chase/product-promo/runs" \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8823-v1" \
  -d '{"instruction": "gift wrap promo", "input": {"url": "https://example.com/1.jpg"}}'

Handling the 409s

For 409 idempotency_key_in_use, wait about one second and send again to get the original run. For 409 idempotency_conflict, stop and log both bodies. Do not retry with a random key, because that is how a duplicate render and a second charge happen.

For bulk queues, the key lives at the batch level: a spent key replayed on a queue create returns the old queue. Keep a per-batch key such as bf26-batch-1 and keep the per-item runs identifiable through each item instruction or input.

Store the receipt data.id the first time you see it, then use the URLs on the receipt for status and result, not paths you construct yourself.

  • Key = order id + version.
  • Never the clock, never a fresh uuid per try.
  • Bump the version only for a deliberate re-run.

Test the replay before the sale

Do this once before launch. Submit one run with a test key, copy the receipt, then submit the identical request again. You should see 200 and idempotency_hit: true with the same run id. Then change one character of the instruction and send it under the same key. You should get 409 idempotency_conflict. If both behave, your retry code is safe to turn on.

Then simulate the crash. Kill the worker after the submit, restart it and let it retry. If it produces a second run, the key is probably built from the clock or a random value. Fix that before the queue holds 100 orders.

Finally, decide who bumps versions. A human or a clearly named job should be the only thing that moves v1 to v2, because that step is the deliberate re-run, and it is the one place a second charge is intended.

Related posts

More in Formats

All Formats posts

Written by Sume