Episode idempotency keys: season, episode and version for Sume runs
Build the Sume Idempotency-Key from series, season, episode and a version you bump on purpose. What replays, what conflicts, what needs a new key.

For an episodic series, derive the Idempotency-Key from what the episode is, not from when you asked: series slug, season, episode number, and a version you bump only when you deliberately want a fresh render, such as brand-series:s01e03:v1. Sume's docs say a uuidgen per request makes the header decorative, because a double click or a redelivered job then turns into two paid runs.
The key is up to 255 characters and is scoped to one Format, so the same string sent to two Formats starts two runs. That matters for a series with a separate Format for the cold open and for the main episode: the same episode key is safe on both.
What each replay does
These rules come from the Format call page and apply to every episode you submit.
| What you send | Result |
|---|---|
| Same key, same body | 200 with the original receipt and idempotency_hit: true; no second run, no second charge |
Same key, different body (even a different instruction or attachment list) | 409 idempotency_conflict; nothing runs |
| Same key, two requests at once | One wins; the other gets retryable 409 idempotency_key_in_use; wait about a second and resend |
Same key after a create that failed (402, 503) | The key was released; fix the cause and retry with the same key |
| No key | Every call starts a new paid run |
A key function
Keep the pieces readable. The embed cookbook hashes its pieces; for a series you usually want to read the key in a log and in the dashboard, so plain text is fine as long as it stays under 255 characters.
def episode_key(series: str, season: int, episode: int, version: int = 1) -> str:
if not series or season < 1 or episode < 1 or version < 1:
raise ValueError("series, season, episode and version are required")
key = f"{series}:s{season:02d}e{episode:02d}:v{version}"
if len(key) > 255:
raise ValueError("Idempotency-Key must be 255 characters or fewer")
return key
assert episode_key("brand-series", 1, 3) == "brand-series:s01e03:v1"
assert episode_key("brand-series", 1, 3, 2) == "brand-series:s01e03:v2"When to bump the version
- Bump it when you change the episode body on purpose, for example a new script. The old key would return
409 idempotency_conflictbecause the body differs. - Bump it to retry a failed run. The old key is bound to the run that already failed, so reusing it returns that same failed receipt.
- Do not bump it for a network retry of the same request. The replay returns the original receipt and you pay once.
- For a reshoot of one scene, continue the run with
previous_run_idand a new key such asbrand-series:s01e03:v1-retry-sc7.
Bulk queues are different
A bulk create takes the key from the Idempotency-Key header or an idempotency_key body field, and the header wins. Replaying the same key with the same { concurrency, items } returns 202 and the existing queue; a different payload returns 409 idempotency_conflict with details.queue_id. Mint a fresh queue key per season batch, because replaying a spent key returns the old queue, not a new one. Per-item keys are not part of the queue envelope, so keep your per-episode keys in your own ledger.
Sources
Related posts
More in Developers
- Extend a video Veo didn't make: which API takes an uploaded clip
Veo 3.1 extension only accepts Veo-made videos. Here is what Gemini Omni and Sume accept instead when you need to continue a clip you uploaded.
- Extend an AI clip by hand: last frame to the next clip, in Python
No extend endpoint? Pull a clip's last frame with Sume's video-frames API and feed it as the first frame of the next job. Python code under 30 lines.
- Fabric audio under 10 MB: how many seconds of wav and mp3 fit
Fabric lip-sync takes Sume-hosted audio under 10 MB, 300 s max. Stereo wav hits the size cap near 56 s; 128 kbps mp3 would reach 625 s, so length binds first.
- Fan out four Omni Flash clips in parallel and poll the jobs
Submit four Gemini Omni Flash requests at once on Sume, store each job id, poll status with backoff, then join the clips with Timeline.
Written by Sume