Sume /v1/videos says cancelled, /v1/jobs says canceled: guard it
The /v1/videos poll uses pending, in_progress and cancelled; /v1/jobs uses queued, processing and canceled. A small normalizer keeps your poller from hanging.

Two routes, two vocabularies
Sume exposes the same video job two ways. POST /v1/videos is shaped like the OpenRouter video API, with a polling_url and its own poll statuses. The same job is also visible at GET /v1/jobs/{id}/status with Sume's job vocabulary. Both are correct; they just do not spell things alike.
The trap is a poll loop whose terminal set came from one page and whose input came from the other. A job that ends canceled but is looked up as cancelled would never match and the loop would run to its deadline.
The two vocabularies (read 2026-10-05)
| Meaning | /v1/videos poll | /v1/jobs status |
|---|---|---|
| Accepted, waiting | pending | queued |
| Running | in_progress | processing |
| Done | completed | completed |
| Error | failed | failed |
| Stopped by request | cancelled | canceled |
A normalizer
Map every spelling to the jobs vocabulary at the edge and test against that. Run this as is:
const MAP = {
pending: "queued", in_progress: "processing",
cancelled: "canceled", canceled: "canceled",
queued: "queued", processing: "processing",
completed: "completed", failed: "failed",
};
export const normalize = (s) => {
const v = MAP[s];
if (!v) throw new Error("unknown Sume status: " + s);
return v;
};
export const isTerminal = (s) => ["completed", "failed", "canceled"].includes(normalize(s));
console.log(isTerminal("cancelled"), isTerminal("in_progress"));Why throw on unknown
An unknown status should stop the loop loudly, not spin. Sume's docs also mention a queue-shaped field (IN_QUEUE, IN_PROGRESS, COMPLETED) on the jobs status envelope; it maps one to one onto sume_status, so pick one field and stay on it.
On a timeout, do not resubmit the paid request. The job keeps running and billing; read it again from the polling URL.
Sources
Related posts
More in Developers
- Sume webhook and status poll race: never move a job row backwards
A late poll can say processing after the webhook already said completed. A rank-guarded SQLite update keeps a Sume job row from going backwards.
- Sume webhook five-minute replay window: reject stale deliveries
Sume signs timestamp.raw_body and advises a five-minute replay tolerance. Reject stale timestamps, refuse an empty secret, and keep job_id as the dedupe key.
- Sume webhook retries: 10 attempts 30 seconds apart, size your downtime
Sume retries a failing webhook up to 10 attempts at a default 30-second spacing, about 4.5 minutes. Past that, redeliver or poll after a publisher outage.
- Swift: submit a Kling 3.0 job and save the MP4 to disk
One Swift file: POST /v1/videos for kling-3, poll the polling_url, follow the content redirect and write out.mp4. A 5 s clip without audio costs $0.70.
Written by Sume