Cancel a queued transcription job: what the 409 means
POST /v1/jobs/{id}/cancel works only before work starts. After that it returns 409 job_generation_already_started and the job finishes and bills normally.

You can cancel a Sume transcription job only while it is still queued. POST https://api.sume.com/v1/jobs/{id}/cancel returns success before generation work starts; once the job is processing, the same call returns 409 job_generation_already_started with details.cancelable: false, and the job completes or fails normally. A cancel of a job that is already canceled is idempotent. Facts here are from Generation admission and Errors and rate limits, read 2026-10-06.
Why the cutoff exists
The OpenAPI description of the cancel route gives the reason: once any external generation task has begun, cancellation is rejected so usage settlement cannot refund work already submitted. Cancellation is a way to drop work you have not started paying for, not a way to stop a running job.
That matters for a bulk transcription run. If a batch is accepted as 120 queued jobs and you realize the audio URLs are wrong, cancel the queued ones first and let the few processing jobs finish.
| Job status | Cancel result | What to do |
|---|---|---|
queued | Canceled; cancelable is true | Cancel the jobs you no longer need |
processing | 409 job_generation_already_started, details.cancelable: false | Wait; the job completes or fails |
canceled | Idempotent success | Nothing to do |
completed / failed | Not cancelable | Read the result or the events |
A cancel loop that respects the 409
Treat the 409 as information, not as an error to retry. List what is still queued, cancel each id, and count the ones that had already started.
import os, requests
API = "https://api.sume.com"
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
def cancel_queued(job_ids):
canceled, started = [], []
for job_id in job_ids:
r = requests.post(f"{API}/v1/jobs/{job_id}/cancel", headers=H, timeout=30)
if r.status_code == 409:
started.append(job_id)
else:
r.raise_for_status()
canceled.append(job_id)
return canceled, startedHow do I check what a canceled job cost?
Ask the ledger instead of assuming. GET /v1/usage?job_id=... returns a summary.debited_usd_micros for that job, and held or refunded amounts are not counted as spend. A job canceled while queued should show nothing debited; one that already started will show its normal capture. The step-by-step is in what one transcription job cost.
Sources
Related posts
More in Developers
- Celery task for an AI video API: submit, poll, retry on Wan 3.0
Two Celery tasks for Sume's /v1/videos: one submits a Wan 3.0 job with an Idempotency-Key, one polls with self.retry(countdown) and stops on a terminal status.
- Check a Shorts timeline body offline: a Python mirror of the rules
Catch Timeline 1.0 refusals you can see in the body (start at 0, 0.5 s coverage, transitions, even sizes) in 28 lines of Python before the plan call.
- Check an image request against the Sume catalog before you send it
Fetch GET /v1/images/models once, then reject a bad ratio, quality or reference count in Python before it reaches POST /v1/images and returns a 400.
- TTS then H3 Max lip sync: check the 5 to 14.8 s audio window
H3 Max lip sync takes 5 to 14.8 seconds of Sume-hosted audio and clips the rest. Measure a TTS line from its word timings and price it before you submit.
Written by Sume