Cancel a GPT Image 2.5 job: only possible before it starts

Sume's POST /v1/jobs/{id}/cancel works only before generation work starts. How to read cancelable, what the 409 means, and what a client timeout does not do.

5 min readSume
All posts

You can cancel a GPT Image 2.5 job on Sume only while it has not started generating. POST /v1/jobs/{id}/cancel succeeds for a job that is still queued and returns 409 once any external generation task has begun, because Sume will not refund work already submitted. The job envelope tells you in advance: cancelable is true only before generation starts, and cancel_url is null afterwards.

So if you sent a wrong prompt and want the money back, speed matters, and a job that is already processing will finish and be billed. The details below come from the OpenAPI cancel description, the jobs docs and generation admission, read 2026-10-03.

When is a job still cancelable?

Sume's queue-first admission means a valid job can sit in queued while your workspace's generation concurrency is full. That waiting time is when cancel works. Concurrency is a plan limit, so a batch larger than your concurrency produces a tail of queued jobs you can still drop.

Cancel is also limited to the member whose key or Agent turn created the job; per the jobs docs, only that member can cancel it.

Job states and cancel behaviour, from Sume's OpenAPI and jobs docs, read 2026-10-03.
Job statecancelableCancel call result
queued, generation not startedtrue200, status CANCELED
processing, generation startedfalse (cancel_url null)409 job_generation_already_started
completed, failedfalse409 job_not_cancelable
Already canceled-200, idempotency_hit: true

How do I cancel from code?

Submit with mode: "async" so you always get the job envelope, keep the cancel_url, and call it only if cancelable was true on your last read. This Python runs against the real routes and prints the outcome.

import os, requests

H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}", "Content-Type": "application/json"}

def main():
    r = requests.post("https://api.sume.com/v1/images", headers=H, timeout=60, json={
        "model": "openai/gpt-image-2.5", "quality": "low", "mode": "async",
        "image_size": "1024x1024", "prompt": "A lighthouse at night, long exposure"})
    r.raise_for_status()
    d = r.json()["data"]
    print("job", d["job"]["id"], "cancelable", d["cancelable"])
    if d["cancelable"] and d["cancel_url"]:
        c = requests.post(d["cancel_url"], headers=H, timeout=30)
        print(c.status_code, c.text[:200])

main()

What does the 409 mean for my bill?

A 409 on cancel means the job is past the point where Sume can stop it. The job keeps running and, if it completes, is billed in full. Failed or canceled generations are not billed, which is why a successful cancel is the only way to avoid the charge on a job you no longer want.

Do not confuse cancel with giving up locally. The docs say a client-side timeout does not cancel the job: it keeps running and still bills, and you have only stopped watching.

What should I do instead of canceling?

If you missed the window, use the result anyway or limit the damage on the next run.

  • Test prompts at quality: "low"; a wrong low-quality job is a small loss.
  • Submit in small waves rather than all rows at once, so a bad prompt is caught while the rest are not yet submitted.
  • Use an Idempotency-Key per operation so a retry after a network fault does not create a second paid job.
  • Check queue_full: a 429 with that code means your queue is full, and finishing or canceling existing jobs frees room.

How do I check a job before cancelling?

Read GET /v1/jobs/{id}/status and look at cancelable and the sume_status field before you call cancel; the status response also carries terminal and next_poll_after_seconds. A job that has already reached a terminal state returns 409 on cancel, so reading first spares you a pointless call. The same read works for a job you submitted in a previous session, as long as you stored its id.

How does this fit a batch workflow?

In a CSV run, jobs beyond your concurrency wait as queued. If you notice a prompt mistake after submitting fifty rows, list your jobs, find the ones still cancelable and cancel those, then fix the prompt and resubmit with new idempotency keys. The ones already processing will complete and bill; plan to accept that cost.

This is another reason to submit in waves. A wave of ten that you can inspect before the next wave limits how many jobs sit in the cancel window with a bad prompt, and keeps the number of unavoidable charges small.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume