Re-ran a UGC variant bulk run and got the old queue: why
Replaying a Sume bulk-run Idempotency-Key with the same body returns 202 and the existing queue, no new videos. A changed body is 409. Mint a key per batch.

If you re-run a Sume bulk request with the same Idempotency-Key and the same { concurrency, items } body, you get 202 and the existing queue back, not a new batch of UGC variants. With the same key and a different body, you get 409 idempotency_conflict, and details.queue_id names the original. To make a new batch, mint a new key.
This is stated in Sume's Bulk runs docs under Idempotency. Keys are scoped to one Format. The replay stays 202 for bulk, unlike a single run, and the queue object has no idempotency_hit field, so you cannot tell a replay from a fresh create by the status code.
What happens for each replay?
Sume's docs give three cases.
| You send | You get | What it means for your variants |
|---|---|---|
| New key, any body | 202, a new queue | New runs, new spend |
| Same key, same body | 202, the existing queue | No new runs; this is the retry-safe case |
| Same key, different body | 409 idempotency_conflict | Nothing new is created; details.queue_id names the original |
Why does it cause surprise?
A script that hard-codes the key, or derives it from the Format name, will quietly return the first queue forever. You edit the hook lines, run again, and still see last week's queue, because the body only changes if the items changed. If the items are identical you get the old queue; if they differ you get a 409. Either way no new variants render until you change the key.
How should I key a batch?
Generate one key per intended batch and store it next to the batch, so a network retry resends the same key and a deliberate re-run gets a new one. The docs also accept idempotency_key in the body, and the header wins when both are present. The sample uses the catalog Format sume-close-camera-ugc called at the reserved sume handle, which any key with formats:write may call.
import json, os, urllib.request, uuid
KEY = os.environ.get("SUME_API_KEY", "")
if not KEY:
raise SystemExit("SUME_API_KEY is empty")
def create_queue(items, idem_key):
body = json.dumps({"concurrency": 3, "items": items}).encode()
req = urllib.request.Request(
"https://api.sume.com/v1/formats/sume/sume-close-camera-ugc/bulk-runs",
data=body,
method="POST",
headers={
"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json",
"Idempotency-Key": idem_key,
},
)
with urllib.request.urlopen(req) as r:
return json.load(r)["data"]["id"]
batch_key = str(uuid.uuid4()) # new key per batch, saved with the batch
What if the retry came from a timeout?
Resend the same body with the same key. You get the queue you already created, so there is no second charge for the same items. Then follow status_url and read counts; completed does not mean all succeeded.
Sources
Related posts
More in Developers
- Veo 3.1 returns one video per request: how to get 4 variants
Google's Veo 3.1 table says one video per request. To get four variants on Sume, send four requests with four Idempotency-Keys, and watch queue_full.
- AI SDK stream cancel on disconnect: the Sume job keeps running
AI SDK 7.0.127 fixes stream cancellation when consumers disconnect. A cancelled stream does not cancel a Sume job: save the job id and read status later.
- AI SDK 7 tool search with deferred tools and Sume tools
AI SDK 7.0.127 lets a search() callback rank eligible deferred tools. Load a few Sume tools up front and fetch the rest by tools_schema on demand.
- Did the edit stay in its region? A pixel-diff check for GPT Image 2.5
After a GPT Image 2.5 edit, measure how much changed outside the area you meant to change. A Pillow script that diffs the result against the original.
Written by Sume