A Python Enum for Sume job statuses and the terminal set
Sume jobs move through queued, processing, completed, failed and canceled. A tested Python Enum and terminal set so a poll loop stops on all three endings.

Sume job statuses are queued, processing, completed, failed and canceled, and the terminal ones are completed, failed and canceled. Model that as an Enum plus a terminal set, and a loop that checks the set cannot hang on a failed or canceled job.
The most common bug is waiting only for completed. The status endpoint also returns a terminal flag and a next_action, so you can use those too; the Enum is for your own code and logs.
The Enum
Constructing JobStatus from a string raises ValueError for anything unplanned, which is what you want when the API grows a new state. The run printed the next step for each of the five.
Using a str-based Enum means values compare equal to the raw strings, so JobStatus('failed') == 'failed' is true and logging prints something readable. Keeping the terminal set in one place also gives you a single line to change if the API ever adds an ending. The sample deliberately raises on an unknown value instead of guessing, because a guess could mean polling forever or, worse, fetching a result for a job that has none. Decide in your own code whether an unknown value should abort the batch or only log and keep waiting until your deadline.
from enum import Enum
class JobStatus(str, Enum):
QUEUED = "queued"
PROCESSING = "processing"
COMPLETED = "completed"
FAILED = "failed"
CANCELED = "canceled"
TERMINAL = {JobStatus.COMPLETED, JobStatus.FAILED, JobStatus.CANCELED}
def next_step(status: str) -> str:
s = JobStatus(status) # raises ValueError on a status you did not plan for
if s not in TERMINAL:
return "poll"
return "fetch_result" if s is JobStatus.COMPLETED else "read_job_error"
for raw in ("queued", "processing", "completed", "failed", "canceled"):
print(raw, "->", next_step(raw))What each ending means
The docs describe next_action values of poll_status, fetch_result and inspect_events. The mapping below is how the sample treats them.
| Status | Keep polling | Next read |
|---|---|---|
| queued | Yes | Wait |
| processing | Yes | Wait |
| completed | No | GET /v1/jobs/{id}/result |
| failed | No | GET /v1/jobs/{id} for the error |
| canceled | No | GET /v1/jobs/{id} |
Why failed and canceled read the record
The result endpoint answers 409 job_not_completed for anything that is not completed, so a failed job has no result to fetch. The reason lives on the job record. Credits for a failed job are refunded.
Tradeoffs
A strict Enum means a new status stops your loop with an exception. Catch it, log the raw value and keep polling until your own deadline, rather than crashing a batch.
If you also consume webhooks, note that their event names differ from statuses: job.completed, job.failed and job.canceled. Map each event to the matching status in one small dictionary, and the polling path and the webhook path then end in exactly the same state.
Sources
Related posts
More in Developers
- Python exceptions for Sume errors: retry on the class, not the status
Map the Sume error envelope code to two exception classes, Retryable and Fatal, so one except clause drives retries. Covers 409, 429, 402 and 503.
- Python: which Sume video models take 20 seconds at 1080p?
A short Python script reads GET /v1/videos/models and filters by duration and resolution, so new launches never break a hard-coded model list.
- Python compare_digest TypeError on a Sume signature header
compare_digest raises TypeError on non-ASCII str. A tested Python verifier for the Sume webhook signature that compares bytes and rejects an empty secret.
- Python pre-flight for a Short: catch Timeline schema errors offline
A Python check for a Sume Timeline body: even width and height, allowed fps, 1 to 1800 seconds, fade limits and start order. Run it before the plan call.
Written by Sume