canceled vs cancelled: one letter that breaks video status checks
Sume native jobs say canceled and send job.canceled. The OpenRouter-shaped /v1/videos response says cancelled. Normalize the spelling before you branch.

Sume spells the status canceled in its own job envelope and its webhook event job.canceled. The OpenRouter-shaped GET /v1/videos/{job_id} response spells the same state cancelled, because that is OpenRouter's wire value. If your poller compares a string against only one spelling, a canceled job looks like an unknown state and your loop either spins forever or marks the item failed. Normalize once, at the edge.
The two vocabularies
OpenRouter's video guide, read 2026-10-05, lists six statuses: pending, in_progress, completed, failed, cancelled and expired. Sume maps its own five job statuses onto that set for the /v1/videos routes and states that expired is in the enum for wire compatibility but never emitted, because Sume does not expire jobs. The native routes GET /v1/jobs/{id}/status and /result use Sume's names.
| Sume `job.status` | `/v1/videos` status | Terminal |
|---|---|---|
queued | pending | No |
processing | in_progress | No |
completed | completed | Yes |
failed | failed | Yes |
canceled | cancelled | Yes |
A one-function fix
The function below accepts either vocabulary and returns the Sume names. It runs as written and has no dependencies.
ALIASES = {
"pending": "queued",
"in_progress": "processing",
"cancelled": "canceled",
}
TERMINAL = {"completed", "failed", "canceled"}
def normalize(status: str) -> str:
return ALIASES.get(status, status)
for s in ("pending", "in_progress", "cancelled", "canceled", "completed"):
n = normalize(s)
print(s, "->", n, "terminal" if n in TERMINAL else "keep polling")Where the spelling bites
Three places show up in practice. A poller written for OpenRouter's docs checks for cancelled and never matches a Sume native result. A webhook handler written for Sume switches on job.canceled and a dashboard that reads the polled job sees cancelled. And a metrics query that counts canceled rows silently misses the other spelling. Cancellation is also idempotent in Sume: canceling a job that is already canceled returns the same canceled job, so a retry of a cancel call is safe.
Do not resubmit the original paid request because your local code does not recognize a status. An unrecognized terminal state is a bug in your parser, and a second submit without the same idempotency key is a second charge.
- Normalize at the boundary where you read the response, not in every branch.
- Treat unknown statuses as non-terminal for a bounded time, then alert.
- Use the same constants in the poller, the webhook handler and the dashboard query.
Sources
Related posts
More in Developers
- Sume 501 capability_not_configured: no job started, no credits spent
501 capability_not_configured means that feature is not connected on the platform. No job starts and nothing is charged. Do not retry; contact support.
- Caption 40 clips in six languages with no language hint: $8
Leave `language` off and Sume's caption job detects it. Forty clips of up to 60 seconds cost $8.00 at $0.20 each; here is the loop and the style trap.
- Caption cue limits: 400 characters, 200 cues, 60 seconds
A caption cue takes 1-400 characters, start of 0 or more, end above start and 60 s or less; a request holds 1-200 cues. Validate locally before the $0.20 job.
- Caption job 400: words, cues, segments, script_text are exclusive
The video captions API accepts only one wording source: words, cues or segments (which skip STT), or script_text (aligned onto STT). Sending two returns a 400.
Written by Sume