Sume /v1/videos has id and generation_id: which one do you store?
Both fields hold the same job id on Sume, so store id. Why generation_id exists and where that id also works: /v1/jobs status and result, webhooks, redeliver.

Store id. On Sume's /v1/videos, generation_id carries the same value as id; the docs example for a completed poll shows job_01HXYZ in both. The field exists because the OpenRouter response shape has both, and a client written from those docs may read either one.
Why two fields
The route is OpenRouter-faithful, so the poll response keeps the OpenRouter field names. OpenRouter separates a job id from a generation id. Sume has a single id per job, so it returns the same string twice. The API source says so in a comment next to the line that sets generation_id.
That means you do not need a lookup table between the two, and you should not build one. Pick id, save it, and treat generation_id as an alias.
| Surface | How the id is used |
|---|---|
| GET /v1/videos/{id} | poll (this is polling_url) |
| GET /v1/videos/{id}/content | download, with optional ?index=N |
| GET /v1/jobs/{id}/status | same job in Sume's own shape (queued, processing, canceled) |
| GET /v1/jobs/{id}/result | same job's result |
| POST /v1/jobs/{id}/webhook/redeliver | re-send the terminal webhook (jobs:write) |
| webhook body job_id | the same id; use it as your idempotency key |
What to put in your own table
A table for video jobs needs very few columns. Keep the Sume id, the Idempotency-Key you sent, the model string you asked for, and the status you last saw. The model you read back can differ from the one you sent: a request for sume/auto is answered with sume/auto, and Sume does not disclose the family that ran.
create table video_jobs (
sume_id text primary key, -- id (== generation_id)
idempotency_key text not null unique,
model_requested text not null,
status text not null, -- pending | in_progress | completed | failed | cancelled
usage_cost numeric(10,2),
error text,
updated_at timestamptz not null default now()
);A client written from the OpenRouter docs
If your client was written against the OpenRouter video docs, it probably reads generation_id somewhere, for example to look up usage after the job. On Sume that read works and returns the job id, so you can leave the line alone. The two things that do change are the base path (https://api.sume.com/v1/videos, with no /api segment) and the model ids, which are bare catalog ids such as seedance-2 and never carry a provider-org prefix.
If you start fresh, drop generation_id from your types or mark it optional: it is present on the poll response, but the 202 submit response carries only id, polling_url, status and model.
Webhook side
When you use callback_url, the delivery is Sume's job envelope with job_id and request_id. Both identify the job; job_id is the key your handler should dedupe on, because Sume can send the same terminal event up to 10 times if your endpoint answers badly. See the callback_url envelope.
- Do not parse the id: treat it as an opaque string.
- The rest of the poll shape is in the status spelling post.
Sources
Related posts
More in Developers
- Which languages? Sume's language fields vs MAI's 23 and 60 counts
Microsoft states 23 languages for MAI-Voice-2.1 and 60 for MAI-Transcribe-2-Streaming. Sume publishes no count; here are its language fields.
- Which request_id do you dedupe a Sume run webhook on?
Dedupe on the envelope request_id, which equals run_id and is stable across retries. The nested payload.request_id is a different id. A code check.
- Which Sume API errors to retry and which to stop on: Node wrapper
A retry policy by error.code for Sume submits: retry rate_limited, queue_full, provider_capacity_exceeded; stop on 400, 401, 402, 409. Node wrapper with jitter.
- Which Sume errors mean switch the video model, and which mean wait
Eleven documented Sume error codes and job categories sorted into three actions: fix the request, retry the same model later, or fall back to another model id.
Written by Sume