Sume idempotency: JSON key order is ignored, array order is not
Resending a Sume job with the same fields in a different JSON key order still returns the original job. Changing an array's order or a value gives a 409.

When you retry a paid Sume request with the same Idempotency-Key, the API compares your payload with the stored one after normalizing it. Object keys are sorted before comparing, so {"a":1,"b":2} and {"b":2,"a":1} count as the same request. Array order is kept, so [1,2] and [2,1] differ and give 409 idempotency_conflict.
What the match covers
The OpenAPI text says a reuse with the same operation and normalized payload returns the original job with idempotency_hit: true, and a different operation or payload returns 409 idempotency_conflict. In the repo, the comparison hashes the job type and the request through a stable stringify that sorts object keys at every depth.
| Change | Same key gives |
|---|---|
| Object keys in another order | Original job, idempotency_hit: true |
| Whitespace or formatting of the JSON | Original job (the body is parsed first) |
| Array items in another order | 409 idempotency_conflict |
| Any value changed | 409 idempotency_conflict |
| A different endpoint | 409 idempotency_conflict |
Reproduce the rule locally
This sketch mirrors the sorted-keys normalization so you can test your own request builder. It is an illustration of the rule, not the server code.
const stable = (v) =>
v === null || typeof v !== "object"
? JSON.stringify(v)
: Array.isArray(v)
? `[${v.map(stable).join(",")}]`
: `{${Object.keys(v).sort().map((k) => `${JSON.stringify(k)}:${stable(v[k])}`).join(",")}}`;
const a = { prompt: "a red kettle", duration: 5 };
const b = { duration: 5, prompt: "a red kettle" };
console.log(stable(a) === stable(b)); // true: key order ignored
console.log(stable({ x: [1, 2] }) === stable({ x: [2, 1] })); // false: array order countsWhy it matters in a retry wrapper
A wrapper that rebuilds the body from a hash map on each retry may emit keys in a different order. That is safe here. A wrapper that shuffles list inputs, such as reference images, is not safe: build the list once and reuse it.
Never regenerate a payload with a fresh timestamp inside it, because a changed value is a different request.
Sources
Related posts
More in Developers
- How long does a Sume Idempotency-Key last? The docs don't say
The Sume docs describe Idempotency-Key replay and the 409 conflict but give no lifetime. What is documented, what is not, and a safe way to design around it.
- Sume job.canceled and job.failed webhooks both say status ERROR
A Sume job.failed and a job.canceled webhook both carry status ERROR and an error object. Branch on the event name, not on status, to tell them apart.
- Sume job status logs_available is false: read events_url instead
A Sume job status carries logs_available, false while diagnostics live behind events_url. Read the events timeline for the lifecycle, not inline logs.
- Sume job webhook: request_id equals job_id, store one
In a Sume job webhook payload, request_id and job_id are the same value. Which to use as your dedupe key, and what else the payload carries.
Written by Sume