Use your ad variant id as the Idempotency-Key on Sume
Name each ad variant once and send that name as the Idempotency-Key. Retries do not double-bill, and the receipt echoes it back so results map to your ad names.

Make the Idempotency-Key a stable name for the variant, such as spring-mug-hookB-endingC-v1, and send it on every submit. Sume's /v1/videos route supports the Idempotency-Key header, and a Format run receipt carries it back in trigger.idempotency_key. A retry after a timeout then returns the existing job instead of creating and billing a second one, and your ad platform naming and your Sume jobs share one string.
What the key does on each route
The behavior differs slightly by route, so check which one you are on. The facts below come from the Sume docs for /v1/videos and for Format runs and bulk queues.
| Route | Same key, same body | Same key, different body |
|---|---|---|
| POST /v1/videos | Supported via the Idempotency-Key header | 409 conflict |
| POST /v1/formats/{handle}/{slug}/runs | 200 with the original run and idempotency_hit true | 409 idempotency_conflict, nothing runs |
| POST /v1/formats/{handle}/{slug}/bulk-runs | 202 with the queue that already exists | 409 idempotency_conflict, details.queue_id names the original |
Choose a naming scheme before you generate
A good key encodes the things you will want to filter on later and a version you bump on purpose. Campaign, hook letter, ending letter and version fit in a short string. Do not put customer data or secrets in a key, because it appears in receipts.
The version suffix is the control you use deliberately. If a clip was bad and you want a new take with the same prompt, bump v1 to v2. If you reuse v1 with the same body you get the old job back, which is the point of idempotency, and you pay nothing extra.
- campaign-hook-ending-v1 for single clips.
- One key per bulk batch, with a fresh value for each new batch, since a replayed bulk key returns the old queue.
- Never reuse a key with an edited prompt; you will get a 409.
Reading the key back
On Format runs, the receipt's trigger object includes source api and the idempotency_key you sent. That lets a webhook handler or a nightly reconciliation job look up the variant by the name you chose, with no side table of job ids. For /v1/videos, store the returned id against your key at submit time, since the 202 body carries id, polling_url, status and model.
Bulk queues have an extra caution. The scope of a bulk key is one Format, and a replay returns 202, not 200, and has no idempotency_hit field. If you resend a batch after an ambiguous failure, compare the returned queue's items with your sheet before assuming it is new.
Example: submit with a variant key
The Node snippet below submits one variant and prints the job id next to the key. Run it twice with the same body and you should see the same id both times.
const key = 'spring-mug-hookB-endingC-v1';
const body = { model: 'seedance-2', prompt: 'Hook B, ending C: mug on a desk', duration: 6, aspect_ratio: '9:16' };
fetch('https://api.sume.com/v1/videos', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SUME_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': key,
},
body: JSON.stringify(body),
})
.then((r) => r.json())
.then((j) => console.log(key, j.id, j.status));Concurrent duplicates and the in-use error
Format run creates add one more case. If the same key arrives twice at the same moment, one request wins and the other gets 409 idempotency_key_in_use, which the docs mark as retryable. Wait about a second and send again, and you receive the original run. This is the situation you hit when a queue worker and a retry timer both fire for the same variant, so a client that treats every 409 as fatal will wrongly abandon a variant that was in fact submitted.
A small rule covers it: on 409 idempotency_key_in_use, sleep and resend the identical body; on 409 idempotency_conflict, stop and look at your code, because you changed something that should have been fixed for that key.
Reconciling against the ad platform
The reason to bother is the join. Ad platforms let you name ads and ad sets, and when a report arrives you want to ask which Sume job made the winner and what it cost. If your ad name and your Sume key are the same string, that is a lookup. If they are not, someone maintains a spreadsheet that drifts.
Keep one table with the key, the Sume job or run id, the model id, the usage amount, and the ad platform ad name. Fill the first four at generation time and the last at upload. Nothing in this table needs a Sume feature beyond the key and the receipt.
Sources
Related posts
More in Developers
- API key scopes for Sume: which key can call which endpoint family?
Sume API keys carry fixed scopes: formats:write, actions:read, agent_completions:write, account:read. Which scope each route needs, and why old keys get a 403.
- Arabic speech to text API: Sume STT with language_code ar
Transcribe Arabic audio with Sume STT: send language_code ar, check the reported language, and review the text. $0.01 per audio minute.
- Avatar video webhook mode: what arrives and what to poll anyway
Use mode webhook for a Sume avatar video and Sume posts one terminal event: completed, failed or canceled. Payload, signature headers and the polling backup.
- Balance check before an ad variant burst: Sume API 402 guard
Read GET /v1/balance before sending a burst of video variants. Sume reserves 1.25 times list price on submit and returns 402 insufficient_credits below it.
Written by Sume