SQLite: add a sume_job_id column to rows that used sora-2 snapshots
OpenAI removed the Videos API and every sora-2 snapshot on Sept 24, 2026. A migration that tags old rows, then re-renders each once through Sume.

Add two nullable columns, sume_job_id and idempotency_key, put a unique index on the key, and re-render only the rows whose model still names a sora-2 id and whose sume_job_id is empty. OpenAI's deprecations page lists the Videos API and five model ids as removed on September 24, 2026, with no replacement named, so any table that stored those ids now points at a service that no longer answers.
Which stored ids are dead
The page notified the removal on March 24, 2026. If your rows hold a dated snapshot, the match must include it; a LIKE 'sora-2%' filter catches all five.
| Id | Removed |
|---|---|
| sora-2 | 2026-09-24 |
| sora-2-pro | 2026-09-24 |
| sora-2-2025-10-06 | 2026-09-24 |
| sora-2-2025-12-08 | 2026-09-24 |
| sora-2-pro-2025-10-06 | 2026-09-24 |
The migration
The key is built from the row id, so it is stable. A job that crashes between the submit and the UPDATE reruns with the same Idempotency-Key, and Sume's docs say a replay returns the original job instead of starting a second one. SQLite allows many NULLs in a unique index, so unmigrated rows do not collide.
import sqlite3
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE renders (id INTEGER PRIMARY KEY, model TEXT, prompt TEXT)")
db.execute("INSERT INTO renders (model, prompt) VALUES ('sora-2-pro-2025-10-06', 'harbor at dawn')")
db.execute("ALTER TABLE renders ADD COLUMN sume_job_id TEXT")
db.execute("ALTER TABLE renders ADD COLUMN idempotency_key TEXT")
db.execute("CREATE UNIQUE INDEX renders_idem ON renders (idempotency_key)")
todo = db.execute(
"SELECT id, prompt FROM renders WHERE model LIKE 'sora-2%' AND sume_job_id IS NULL"
).fetchall()
for row_id, prompt in todo:
key = f"sora-port-{row_id}" # stable per row: a crashed rerun sends the same key
db.execute("UPDATE renders SET idempotency_key = ? WHERE id = ?", (key, row_id))
# POST /v1/videos with header Idempotency-Key: key, then store the id it returns
db.execute("UPDATE renders SET sume_job_id = ? WHERE id = ?", ("job_from_submit", row_id))
print(db.execute("SELECT id, sume_job_id, idempotency_key FROM renders").fetchall())Limits to plan for
The sample does not call the API; the comment marks where POST /v1/videos goes. A re-render is a new generation, not a recovery of the old clip: /v1/videos returns 400 for seed, so you cannot reproduce the old output, and the model, length and look will differ. Pick the model from GET /v1/videos/models using the supported_durations and supported_resolutions your rows need.
Reuse a key only for an identical payload. If you change the prompt for a row, give it a new key, or the API answers 409 idempotency_conflict.
Sources
Related posts
More in Developers
- SQLite ledger for Sume bulk queues: SKU to run id and what to retry
Record each bulk queue item by index in SQLite, keep the SKU you sent, and query the SKUs that failed or were canceled and never completed in a later queue.
- Start a Sume render from a serverless function: submit, save, 202
A function must not wait for a video. Submit with mode webhook and a stable Idempotency-Key, save the status URL, return 202, and let the signed webhook finish.
- Stream a product feed NDJSON into 100-row Sume bulk bodies
Read an NDJSON product feed lazily with itertools.islice and write one bulk-run body per 100 rows, so a large file never has to fit in memory. Python stdlib.
- Retry or fix? A status-code policy for Sume API clients in Python
Retry 429 and 503 with the same idempotency key; never retry 400, 401, 402 or 404. A small Python policy function, tested, with the Sume docs rules behind it.
Written by Sume