Stalled or just slow? Read the Sume Format run events feed
A Format run takes minutes. GET /v1/format-runs/{id}/events shows the phase, and the last entry's at timestamp is a progress clock. If it stops, it is stalled.

Call GET /v1/format-runs/{run_id}/events. It lists phases (preparing, running, finalizing) with a status and a duration. Consecutive entries with the same phase and status merge, and their at keeps advancing. So at on the last entry is the progress clock: if it has not moved for several minutes, the run is stalled, not slow.
What the feed looks like
This is the example from the runs page. preparing is all the work before the agent gets the run. running is the agent following the recipe, and most of the time goes here. finalizing is teardown and output harvest. Each entry has a status of pending, running, done, warning, error or skipped.
{
"data": [
{ "at": "2026-08-23T23:23:41.000Z", "phase": "preparing", "status": "done", "duration_ms": 1840 },
{ "at": "2026-08-23T23:31:12.000Z", "phase": "running", "status": "running", "duration_ms": null },
{ "at": "2026-08-23T23:40:57.000Z", "phase": "finalizing", "status": "done", "duration_ms": 620 }
]
}Slow is normal
Long-form video is 15 to 30 minutes of work, so a poll every second tells you nothing and spends read budget. Double the gap up to one minute. A 429 or 503 during your loop is temporary: the run continues to execute and to spend, so wait and poll again rather than assuming it failed.
The bounds Sume enforces
queue.position is always null, because Sume does not publish queue depth. If runtime_unavailable lasts more than a few minutes, send a support ticket with the request_id.
| Signal | Meaning |
|---|---|
expires_at on a non-terminal receipt | Deadline after which Sume force-finalizes the run as failed |
90 minutes from created_at | The outer bound |
| Older than 25 minutes and silent for 10 | Finalized earlier |
queue.state: waiting | Pickup is inside the normal window |
queue.state: runtime_unavailable | Waited longer than the window and nothing claimed it; read retry_after_seconds |
A simple watchdog
Do not invent your own timeout. Use expires_at as the ceiling. Inside it, read the events feed on a back-off and alarm on a flat at.
When a run is stalled, cancel it with POST /v1/format-runs/{run_id}/cancel and start a new one with a new idempotency key. A canceled run never delivers a webhook, so your receiver will not hear about it.
- Alarm when the last
atis more than several minutes old andstatusis stillrunning. - Alarm on
queue.state: runtime_unavailable. - Do nothing on a long
runningphase with a moving clock.
Webhook as the fast path
The webhook is the quick way to learn the run finished, and a read of result_url is the backup for the day your endpoint is down. A failed delivery never changes the run: after ten refused attempts you have a failed delivery and a run that is still completed.
Related posts
More in Formats
- Turn a new video model launch into a repeatable Sume Format
A new video model dropped and one test clip looked great. Save the recipe as a Format, call it with a key, a cap and a webhook, and keep the result repeatable.
- Formats shared with my workspace: GET /v1/format-grants inbox
GET /v1/format-grants lists grants shared with your team workspace, pending and accepted, newest first. A personal key sees an empty list, not an error.
- Which Sume Format for holiday product video? Read its io profile
Do not guess from the slug. GET /v1/formats/sume/{slug} returns description, io profile and a verified showcase, so you can match input shape to your catalog.
- Which Sume catalog Format for which holiday ad: 27 slugs by job
Sume's first-party Format catalog has 27 slugs at the sume handle. Match product commercials, UGC, try-on and text-led ads to the right one, and read io first.
Written by Sume