Know a Sume bulk batch is done: count terminal webhooks vs item count
A Sume bulk queue has no webhook. Put a webhook_url on each item, verify the signature, dedupe by request_id and count terminal events to the item count.

How do I get a done signal for a whole bulk batch?
The queue itself sends nothing. communication.webhook_url is per item, so each child run delivers its own format.run.terminal event when it finishes. The queue's own status is only available by polling GET /v1/format-run-queues/{id}, where completed means every item is terminal, not that every item worked.
To get a push-style batch signal, count. Know the item count when you submit, count distinct terminal events as they arrive, and call the batch done when the two match.
What must the receiver do?
- Verify the signature on the raw body: header
x-sume-webhook-signatureissume-v1=<hex>, an HMAC-SHA256 of<timestamp>.<raw body>usingx-sume-webhook-timestamp, within 300 seconds. Refuse an empty secret. - Dedupe on the envelope
request_id, which equals the run id and is the same on every retry. - Read
outcome(ok, degraded or error) to tally successes and failures. - Treat items that never started (
run_id: null) as terminal failures on your side; they send no webhook.
What does the counter look like?
This standalone sketch verifies, dedupes and counts. Because it has no web framework, call handle(raw, timestamp, signature) from your own route.
import hashlib, hmac, json, os, time
SECRET = os.environ.get("SUME_WEBHOOK_SECRET", "")
seen, outcomes = set(), {}
def handle(raw: bytes, ts: str, sig: str) -> bool:
if not SECRET:
raise RuntimeError("refusing to run with an empty secret")
if abs(time.time() - int(ts)) > 300:
return False
mac = hmac.new(SECRET.encode(), ts.encode() + b"." + raw,
hashlib.sha256).hexdigest()
if not hmac.compare_digest("sume-v1=" + mac, sig):
return False
ev = json.loads(raw)
if ev["event"] == "format.run.terminal":
seen.add(ev["request_id"])
outcomes[ev["request_id"]] = ev.get("outcome")
return True
def batch_done(expected: int) -> bool:
return len(seen) >= expected
What about stragglers?
Late events are normal: a run can take many minutes, and with concurrency 4 a 100-item batch dispatches in 25 waves. Do not fire your downstream step on a timer; fire it when the counter reaches the expected number, and keep a polling fallback so a lost webhook cannot hold the batch forever.
If the count stalls, poll the queue and read counts and each item's run_id; a run whose webhook failed can be redelivered with POST /v1/format-runs/{id}/webhook/redeliver. Set the expected count to the items that got a run id, since the others will never send an event.
Sources
Related posts
More in Formats
- Localized video batch: one Sume bulk queue per language
Split a multilingual batch into one Sume bulk queue per language so each language gets its own concurrency, spend cap, idempotency key and retry.
- Make Iterator and Array aggregator: build a Sume bulk items array
In Make, an Iterator splits an array into bundles and an Array aggregator joins them. Use the pair to turn sheet rows into a Sume bulk items array.
- A gift portrait for your store homepage: model product portrait Format
The sume-model-product-portrait Format returns one finished still of a person with your product. Use it for a homepage hero, not for a motion ad.
- November 1 switch: restyle Halloween product stills into a fall set
After Halloween, run your stills through the sume-restyle Format in one bulk queue of up to 100 items and keep the product while changing the mood.
Written by Sume