Deterministic Sume Idempotency-Key: body hash plus a take counter

Hash customer, take number and canonical body into one key. A retry replays the job; a deliberate redo bumps the take. Python, stdlib only.

4 min readSume
All posts

Build the Idempotency-Key from a SHA-256 of the customer id, a take number and the canonical JSON body. A retry of the same request then sends the same key and gets the original job back. A deliberate redo with the same body bumps the take, so it gets a new key and a new paid job. This design removes the 409 idempotency_conflict, because a changed body always yields a changed key.

What the API does with the key

The key is 67 characters here, so it fits the limit with room to spare.

Idempotency-Key behavior from the Sume docs, read 2026-10-08
CaseResult
Key is 1 to 255 printable ASCII charactersAccepted
Same key, same bodyThe original job is returned
Same key, different body409 idempotency_conflict
Another request holds the keyidempotency_key_in_use, retry after about 1 s
Create fails with 402 or 503The key is released

The helper

json.dumps with sorted keys and compact separators makes the body canonical, so two dicts with the same content in a different order hash the same. The asserts show the three properties that matter.

import hashlib, json

def idempotency_key(customer, body, take=1):
    canon = json.dumps(body, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
    digest = hashlib.sha256(f"{customer}|{take}|{canon}".encode()).hexdigest()
    return f"v1-{digest}"  # 67 printable ASCII characters, well under 255

a = {"prompt": "A ceramic mug", "resolution": "720p", "duration": 8}
b = {"duration": 8, "resolution": "720p", "prompt": "A ceramic mug"}
assert idempotency_key("acme", a) == idempotency_key("acme", b)     # same intent
assert idempotency_key("acme", a) != idempotency_key("acme", a, 2)  # a second take
assert idempotency_key("acme", a) != idempotency_key("other", a)
print(idempotency_key("acme", a), len(idempotency_key("acme", a)))

The trade-off

  • A changed body gives a new key, so an edited prompt is a new paid job instead of a 409 you can see. If you prefer the conflict as a safety net, key on a business id and not on the body.
  • Store the take number with the order. If you lose it, a retry may start at take 1 and replay the old job, which is usually what you want.
  • Do not put secrets or personal data in the key source. Only the SHA-256 digest is sent, but keep the inputs out of your logs.
  • A replayed job comes back with idempotency_hit, which you can count.

When to use a business id instead

If your system already has an order id or a render id that is unique per intended job, use that as the key source and skip the hash. A business id is easier to search in your logs. The hash is the better choice when no such id exists, or when you want the key to change if and only if the request changes.

Either way, keep the key stable across retries, send it on every attempt, and treat a failed create as a released key. After a 402 you can fund the account and send the same key again.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume