Result 409 on Sume: job_not_completed vs run_not_completed
Both mean not ready: jobs answer 409 job_not_completed, runs 409 run_not_completed with details.status. Poll status_url, then read result_url once.

Reading a result too early gives a 409 on both Sume surfaces, with a different code on each: 409 job_not_completed from GET /v1/jobs/:id/result and 409 run_not_completed from GET /v1/format-runs/{run_id}/result. Neither is a failure of the work. Poll the status endpoint until the item is terminal, then read the result once.
The API answers with a conflict on purpose, instead of returning an empty result that looks like success.
The two codes next to each other
The job rule is on the Sume jobs page and the run rule on the Format runs page (read 2026-10-03).
| Generation job | Format run | |
|---|---|---|
| Result path | GET /v1/jobs/:id/result | GET /v1/format-runs/{run_id}/result |
| Early read | 409 job_not_completed | 409 run_not_completed, with details.status |
| Status path | GET /v1/jobs/:id/status | GET /v1/format-runs/{run_id}/status |
| Ready signal | terminal and result_ready are true | status is completed or failed |
| Failure is read from | The job record: GET /v1/jobs/:id | The terminal receipt: error and output_error |
What to poll on
For jobs, branch on the booleans terminal and result_ready, or on sume_status. The status endpoint also returns a queue-shaped status (IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED, CANCELED) that maps onto sume_status, but do not mix the two vocabularies in one condition.
A failed or canceled job has no result to read, so asking for it also returns the 409; read the failure off the job record instead. For runs, the lifecycle is queued, processing, then completed, failed, canceled or skipped, and next_action on the receipt says poll_status, retry_later or none.
Use the URLs on the envelope instead of building paths. A job envelope carries status_url, result_url, events_url and cancel_url, and next_poll_after_seconds while it is waiting; a run receipt carries the same four URLs.
JOB=job_123
until [ "$(curl -s https://api.sume.com/v1/jobs/$JOB/status \
-H "Authorization: Bearer $SUME_API_KEY" | jq -r .terminal)" = "true" ]; do
sleep 5
done
curl -s https://api.sume.com/v1/jobs/$JOB/result \
-H "Authorization: Bearer $SUME_API_KEY"Where the loop should stop
The loop above has no timeout, so wrap it with a deadline of your own, such as 20 minutes for video. A client deadline does not cancel the job; it only stops you watching. For the full backoff version, see poll on next_poll_after_seconds, and for a batch read where some ids are still running, partial success on batch results.
Sources
Related posts
More in Developers
- Retake one narration line: why reusing the key returns 409
Resubmitting a changed TTS script under the same Idempotency-Key returns 409 idempotency_conflict, and an identical retry returns the original job. Key naming.
- Reverse a video by API: FFmpeg reverse is not on Sume's allowlist
FFmpeg's reverse filter buffers a whole clip. Sume's video-filter allowlist has no reverse and no setpts, and the free check shows it. What to do instead.
- Sume run webhook 3xx redirect: a failed attempt, not a delivery
Sume does not follow redirects on run webhooks, so a trailing-slash 301 fails every attempt. How to find it with a no-follow probe and register the final URL.
- reqwest timeout versus read_timeout for Sume polls and downloads
reqwest has no timeout by default. timeout() is a total deadline including the body; read_timeout() resets per read. Which to use for Sume status polls.
Written by Sume