Wan 3.0 draft and final need different Idempotency-Keys (409)
Reusing one Idempotency-Key for a 480p draft and a 1080p final of the same prompt returns 409 idempotency_conflict. A Python key builder that avoids it.

When you re-render a Wan 3.0 draft at final quality, change the Idempotency-Key. Sume's Generation admission page lists 409 idempotency_conflict for a key reused with a different operation or payload, and the 480p draft and the 1080p final differ in resolution. The key belongs to one exact request; reuse it only to retry that request. A retry of the draft costs nothing extra, and a 30-second 1080p final costs $7.50 against $1.875 for the draft.
A key that encodes what the request is
Build the key from the fields that change the result. Two requests with the same fields give the same key, so retries are safe; a changed field gives a new key.
import hashlib, json
def idem_key(shot: str, body: dict, version: int = 1) -> str:
fields = {k: body[k] for k in ("model", "prompt", "duration", "resolution", "aspect_ratio")}
digest = hashlib.sha256(json.dumps(fields, sort_keys=True).encode()).hexdigest()[:12]
return f"{shot}-{body['resolution']}-v{version}-{digest}"
draft = {"model": "wan-3.0", "prompt": "Slow push-in on a sneaker", "duration": 30,
"resolution": "480p", "aspect_ratio": "9:16"}
final = {**draft, "resolution": "1080p"}
print(idem_key("sneaker-ad", draft))
print(idem_key("sneaker-ad", final))
assert idem_key("sneaker-ad", draft) != idem_key("sneaker-ad", final)What each outcome means
| You send | Result |
|---|---|
| Same key, exact same payload | The original job comes back; no second reserve |
| Same key, different resolution | 409 idempotency_conflict |
| New key, same payload | A new job and a new reserve |
| No key on a retried POST | A second job is possible; keep the key |
The cost of getting it wrong
A conflict is a rejected call, not a charge, so the failure is annoying rather than expensive. The expensive error is the opposite one: minting a fresh key on every retry of a timed-out submit, which can start two jobs for the same shot. At 1080p, two 30-second jobs cost $15.00.
Persist the key with the shot before you send the request, so a crash and restart reuses it.
Storing the key
Write the generated key into your database row for the shot before you send the request, and read it back on retry. If you generate it from a hash of the request fields, as above, the key is reproducible even if the row is lost.
Version bump for a deliberate re-run
Submitting the same fields under a new version starts a new job on purpose, which is how you ask for another take. Use that when you want another attempt, and keep the version unchanged when you are only retrying a failed call.
Sources
Related posts
More in Developers
- Webhook signature mismatch: Sume fingerprint vs OpenRouter t=,v1=
A video webhook that fails verification has three usual causes: parsed body, wrong secret, wrong header format. Sume adds a secret fingerprint header.
- Test a video webhook receiver before the first job: Sume vs fal
Sume sends a signed dummy webhook.test with one API call. fal retries real results up to 31 times and treats 3xx as failure, so test your URL first.
- 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 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.
Written by Sume