Use the idempotency_key on Sume job objects as your join label
Every Sume job object returns the idempotency_key it was created with, and list order is newest first. Join your own labels on that key, not on array position.

Every Sume job object includes the idempotency_key it was created with, and GET /v1/jobs lists newest first, which is not your submission order once retries happen. Join your own records to jobs on idempotency_key, and never on array position. The key is returned on job objects but is never echoed inside an error body.
What the API says
The list endpoint's description says newest-first is not your submission order once a wave retries, so identity must not be read off array position, and that each row carries the key it was created with. The submit schema says the key doubles as the label you join on when rebuilding which job was which.
| Join on | Safe? | Why |
|---|---|---|
| Array position in the list | No | Newest first, and retries reorder |
| Prompt text | No | Two jobs can share a prompt |
idempotency_key | Yes | You chose it, and every job echoes it |
| Job id you stored at submit | Yes | Fine when the submit response was saved |
Rebuild a map after a crash
If your process died before it stored job ids, list the jobs and index them by the key you derived from your own record.
def by_label(jobs: list[dict]) -> dict[str, dict]:
# idempotency_key is on every job object; array position is not identity
return {j["idempotency_key"]: j for j in jobs if j.get("idempotency_key")}
jobs = [{"id": "job_b", "idempotency_key": "ad-2"}, {"id": "job_a", "idempotency_key": "ad-1"}]
print(by_label(jobs)["ad-1"]["id"])Design the key for this use
- Make it deterministic from your record, such as
ad-<order id>-v2, so a rebuild can compute it again. - Keep it under 255 printable characters: longer keys or control characters get a 400.
- Remember that keys are scoped to the member who owns the API key.
Sources
Related posts
More in Developers
- User closed the tab mid-render: cancel the Sume job or let it finish?
Cancel works only before generation starts; after that you get 409 job_generation_already_started and the job bills. A tested tab-close handler.
- v0 MCP tool approval and Sume: ask first before paid calls
v0 lets you disable tools and set ask-for-approval or auto-use per MCP. Pair that with Sume's dry_run and max_spend_usd so a paid call never runs unreviewed.
- callback_url on Sume /v1/videos: HTTPS only, not OpenRouter events
POST /v1/videos takes callback_url. It must be HTTPS, and Sume sends its own job.completed envelope, not OpenRouter's video.generation events.
- Verify a Sume TTS transcript_receipt SHA-256 yourself in Python
Recompute submitted_transcript_sha256 from your script with NFC and LF canonicalization and compare it to the transcript_receipt on a finished Sume TTS job.
Written by Sume