Ideogram 4.5 low to high on one order: new Idempotency-Key or a 409
A reused Idempotency-Key with a different payload returns 409 idempotency_conflict. Key on order id plus a payload hash so a quality change is a new job.

A common flow with Ideogram 4.5 is a cheap quality: "low" draft followed by a high final for the same order. If both calls use the key order-8823, the second is a different payload under the same key, and Sume answers 409 idempotency_conflict. The error details carry the original job's id, status_url and result_url, so you can tell what the key already belongs to.
The conflict is the safety working. The fix is not to retry; it is to name the two calls differently.
Key from the order and the payload
Hash a canonical form of the payload so field order cannot change the key, and keep the result well under the 255 printable character cap.
import hashlib, json
def image_key(order_id: str, payload: dict) -> str:
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
return f"{order_id}-{hashlib.sha256(canonical.encode()).hexdigest()[:16]}"
base = {"model": "ideogram/ideogram-v4.5", "prompt": "Cafe menu poster", "resolution": "1K"}
draft = {**base, "quality": "low"}
final = {**base, "quality": "high"}
print(image_key("order-8823", draft))
print(image_key("order-8823", final)) # new quality, new key, new job
print(image_key("order-8823", draft) == image_key("order-8823", dict(reversed(draft.items()))))What each key means
| Situation | Result |
|---|---|
| Same key, same payload (a network retry) | Treated as the same request |
| Same key, different payload | 409 idempotency_conflict, original job in details |
| Key still held by an in-flight request | 409 idempotency_key_in_use, retryable |
| Submit refused with 429 queue_full | Key is released; replay it |
Practical rules
- Include
model,quality,resolution,nand the prompt in the hashed payload. - A deliberate quality upgrade is a new, separately billed job. That is what you want for a draft then final flow.
- On
idempotency_key_in_use, wait and retry the same call; onidempotency_conflict, do not. - Store the key before you send, so a crash can resume with the same key.
Sources
Related posts
More in Developers
- Keep a series voice consistent: pin model, voice, speed and volume
A series sounds the same only if every episode sends the same TTS settings. Keep one profile in code, pin a model id, and send it with each Sume request.
- Same volume every Shorts episode: gain_db, duck_db, one music bed
Sume does not measure loudness. To keep a series consistent, pin audio.gain_db, soundtrack gain_db and duck_db in one function with one bed. Python plan loop.
- Ktor webhook route for an AI video job: receiveText and HMAC SHA-256
A Ktor route reads the raw body with call.receiveText(), checks Sume's sume-v1 HMAC over timestamp.body in Kotlin and returns 401 when the secret is empty.
- Label speakers without diarization: transcribe each mic track, merge
Sume STT has no speaker labels. If you record each speaker on a separate track, transcribe both tracks and merge the segments by start time. Python script.
Written by Sume