v1/videos job statuses and a failed-job error handler
Map /v1/videos statuses (pending, in_progress, completed, failed, cancelled) to actions, and decide which failed-video errors deserve a retry. Python handler.

On GET /v1/videos/{id}, pending and in_progress mean wait, completed means download, and failed or cancelled means stop and read the error. A failed job is a result, not an exception, so branch on the status and decide from the error category whether a retry makes sense.
The same job is visible at GET /v1/jobs/{id}/status with the Sume vocabulary: queued, processing, completed, failed, canceled. The two sets map one to one, so use whichever surface you started on.
The status map
The OpenRouter-style set on /v1/videos is what you poll. The Sume set is what webhooks and the jobs routes use. Note the spelling: cancelled on /v1/videos, canceled on the jobs routes.
| /v1/videos | /v1/jobs | Terminal | Action |
|---|---|---|---|
| pending | queued | No | Keep polling, queued is normal |
| in_progress | processing | No | Keep polling |
| completed | completed | Yes | Download the clip |
| failed | failed | Yes | Read the error, retry by category |
| cancelled | canceled | Yes | Nothing to download |
Which failures to retry
Failed jobs carry public error metadata with a category. The docs give a next action for each. Validation and generation-rejected errors need a changed input, so a retry only repeats the failure. Queue, generation-unavailable, and runtime-unavailable errors are worth a delayed retry.
| Category | Retry? | Next step |
|---|---|---|
| validation | No | Correct the input |
| quota | No | Add funds or lower the cost |
| generation_rejected | No | Check events, fix the unsupported input |
| queue | Yes, later | Same idempotency key |
| generation_unavailable | Yes, later | Wait and retry |
| generation_timeout | Poll first | Poll the status, then retry later |
| internal | No | Contact support with the request id |
A handler
The function returns a short action string that your queue worker can act on.
import os, requests
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
RETRY_LATER = {"queue", "generation_unavailable", "runtime_unavailable"}
def decide(job_id: str) -> str:
r = requests.get(f"https://api.sume.com/v1/jobs/{job_id}", headers=H, timeout=30)
r.raise_for_status()
job = r.json()["data"]["job"]
status = job.get("status")
if status in ("queued", "processing"):
return "wait"
if status == "completed":
return "download"
if status == "canceled":
return "drop"
category = (job.get("error") or {}).get("category")
return "retry-later" if category in RETRY_LATER else "fix-or-escalate"
print(decide("job_123"))Sources
Related posts
More in Developers
- Validate clip length against supported_durations before a Sume submit
Durations differ by model: some start at 2 seconds, some stop at 10, some reach 30. Read supported_durations from the catalog and snap a request in Python.
- Validate Wan 3.0 reference limits in Python before submitting
wan-3.0 takes 10 images, 5 videos (15 s total, 16 fps minimum) and 5 audios (15 s total). A short Python check catches an over-limit manifest early.
- Verify Sume job webhooks in Express: raw body, sume-v1, replay window
Verify a Sume job.completed webhook in Express with @sume-com/sdk verifyWebhook: raw body, empty secret refused, dedupe on job_id, answer 2xx fast.
- Verify a Sume webhook in Python when the secret is rotating
A Sume webhook can carry two sume-v1 signatures for 24 hours after rotation. A FastAPI verifier that accepts either, rejects an empty secret, checks the clock.
Written by Sume