Weekly specials video for every restaurant location: one bulk run

One bulk run of up to 100 Format runs makes a weekly specials video per location. Concurrency 1 to 16, per-item webhooks, and the counts.failed check.

5 min readSume
All posts

A restaurant group that wants a weekly specials video for each location can queue them as one bulk run: a single POST that holds up to 100 ordinary Format runs, with 1 to 16 of them in flight at a time. Each location is one item. You poll one queue instead of babysitting a dozen calls.

What Sume does not do is know your menu. The recipe is a Format you saved, and the dish, price and photo for each location go in as input. Bulk runs only handle the fan-out.

What goes in each item?

An item has the same body as a single run on Create a run: at least one of instruction, input, previous_run_id or attachments. For specials, that means the location's dish, price and a photo URL in input, and a short instruction that says what to make. The input object takes up to 64 top-level keys, so one object per location is plenty.

Give every item its own communication.webhook_url if you want a callback, because the queue itself has none. Set generation_spend_cap_usd per item too, so one runaway location cannot eat the batch budget.

import json, os, urllib.request

locations = [
    {"id": "mission", "dish": "Crab toast", "price": "$14", "photo": "https://example.com/mission.jpg"},
    {"id": "harbor", "dish": "Clam chowder", "price": "$12", "photo": "https://example.com/harbor.jpg"},
]
items = [{"instruction": "Make this week's 9:16 special video. Show the price card.",
          "input": loc, "generation_spend_cap_usd": 10} for loc in locations]
body = {"concurrency": 4, "items": items}
key = os.environ.get("SUME_API_KEY")
if not key:
    raise SystemExit("set SUME_API_KEY")
req = urllib.request.Request(
    "https://api.sume.com/v1/formats/yourgroup/weekly-special/bulk-runs",
    data=json.dumps(body).encode(), method="POST",
    headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json",
             "Idempotency-Key": "specials-2026-w40"})
print(urllib.request.urlopen(req).read().decode())

How do I know which location finished?

The 202 receipt is a format.run_queue with counts and an items[] array in the same order you submitted. Item 0 is your first location, so keep a list of location ids by index. Each item gets a run_id once it starts, and GET /v1/format-runs/{run_id} returns the full receipt with the output.

Poll status_url, or GET /v1/format-run-queues/{queue_id}. The queue is completed when every item is terminal, and that is where people go wrong.

Why is completed not the same as succeeded?

A queue marked completed can contain failed and canceled items. Read counts.failed and counts.canceled before you tell anyone the specials are ready. A failed item shows format_run_failed, and the reason is on that child's run receipt, not on the queue row. A child that could not even start has run_id: null and a create-run error such as format_run_failed_to_start, and the rest of the queue keeps going.

If you need typed results rather than a paragraph, bind an output_schema per item so each receipt carries the video URL in a known field. Structured output explains the rules, including that every property must be listed in required and optional values are nullable unions.

What are the traps?

  • Mint a fresh Idempotency-Key for each weekly batch. A replayed key with the same payload returns 202 and the old queue, and the same key with a changed payload is 409 idempotency_conflict. A key like specials-2026-w40 is both readable and unique per week.
  • One bad item fails the whole create with 400 invalid_request and details.index, before any queue exists. Nothing is dispatched, so you can fix item 7 and resend.
  • There is no cancel-queue or list-queues endpoint. To stop one location, POST /v1/format-runs/{run_id}/cancel on that child, which frees its slot.
  • Child runs still obey wallet balance and workspace generation concurrency, so a window of 16 does not mean 16 simultaneous renders if your plan allows fewer.
  • The key must carry formats:write to create and formats:read to poll. A service-account key cannot create runs.

For a 12-location group a window of 4 to 6 is a sensible start. Check the queue after the first few items finish, look at one output, and only then leave the rest overnight. The daily price-change variant, where a single clip is re-captioned each day, is covered in Restaurant daily special video.

Sources

Related posts

More in Use cases

All Use cases posts

Written by Sume