Hash the avatar payload into your Idempotency-Key, adopt the 409 job
Hash the avatar request into the Idempotency-Key: a retry returns the same job (idempotency_hit true); a 409 names the job holding the key.
For batch avatar work, build the Idempotency-Key from a hash of the request body. A repeat of the same request then returns the original job with idempotency_hit: true instead of billing a second render, and a changed script produces a new key and a new job. If Sume answers 409 idempotency_conflict, the error details name the job that already holds the key, so you adopt it instead of creating another.
What the contract says
The OpenAPI defines Idempotency-Key as an optional header of 1-255 printable ASCII characters on paid job creation. Reusing a key with the same operation and normalized payload returns the original job with idempotency_hit: true. Reusing it for a different operation or payload returns 409 idempotency_conflict, and error.details names the holder: job_id, job_type, job_status, status_url and result_url. The docs add that you should reuse a key only for an exact retry.
| You send | Sume does | Your move |
|---|---|---|
| Same key, same payload | Returns original job, idempotency_hit true | Keep polling that job |
| Same key, different payload | 409 idempotency_conflict with the holder job | Adopt the holder or choose a new key |
| New key, same payload | Creates a new paid job | Avoid, this double-bills |
| No key, network timeout | Unknown whether a job exists | Never resubmit blind |
Key from the payload
A key derived from the content makes duplicate detection automatic in a queue worker. Serialize the body with sorted keys, hash it, and prefix it with the purpose. 255 characters is plenty for a 64-character digest. Add an explicit version when you want a deliberate fresh render of an unchanged script.
import hashlib, json, os, urllib.error, urllib.request
def key_for(body, version="v1"):
blob = json.dumps(body, sort_keys=True, separators=(",", ":"))
return "avatar-" + version + "-" + hashlib.sha256(blob.encode()).hexdigest()
def submit(body):
req = urllib.request.Request(
"https://api.sume.com/v1/avatar-1.0/talking-video",
data=json.dumps(body).encode(), method="POST",
headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": key_for(body),
"User-Agent": "idem/1.0"})
try:
with urllib.request.urlopen(req, timeout=60) as r:
data = json.load(r)["data"]
return data["job"], data["idempotency_hit"]
except urllib.error.HTTPError as e:
err = json.load(e).get("error", {})
if e.code == 409 and err.get("code") == "idempotency_conflict":
return err["details"], True
raiseCautions
- The hash is over your body, but Sume compares a normalized payload. Two bodies that you hash differently may still count as the same on the server, and the reverse is not true, so a content hash errs on the safe side.
- Do not put the script text or personal data in a key. Hash it. Keys can appear in logs and URLs on your side.
- A hit means the job exists, not that it finished. Read
next_actionand poll or wait for the webhook. - If a job failed and you want to try again with the same body, change the version suffix on purpose so the retry is a visible decision and not an accident.
When this is not enough
Idempotency covers retries of the submit call. It does not deduplicate two different scripts that say the same thing, and it does not protect you from approving a wrong preview. Keep a record of avatar_video_id, key, and script version so a reviewer can trace each file back to the request that made it.
Sources
Related posts
More in Developers
- Detect a clip's language before dubbing with Sume STT
Run Sume STT with no language hint and read language_code and language_probability from the result. A short Python check that gates a dub on a confident answer.
- Detect Sume API changes in CI: diff the live OpenAPI document
Pull https://api.sume.com/reference/json in CI, reduce it to sorted method and path lines with jq, and fail the build when the route list changes.
- Idempotency-Key from a sha256 of the body: Seedance 2.5 retries
Retry a timed-out POST /v1/videos without paying twice by deriving Idempotency-Key from the request body. Python code plus the 409 conflict rule on Sume.
- Burned-in captions and YouTube AI disclosure: captions are exempt
YouTube's disclosure page names caption creation among edits that need no altered-content label. What that covers when Sume burns captions into a Short.
Written by Sume