Cancel an Agent Completion at a deadline: Python poll loop

A Python loop that starts a Sume Agent Completion, polls status_url until next_action stops saying poll_status, and cancels at a deadline.

6 min readSume
All posts

Start the run, store its status_url and cancel_url, poll until next_action stops being poll_status, and if your deadline passes first, POST the cancel URL and report the run as canceled. The loop below does that with the standard library. Cancel stops a run that is still in flight, so pair the deadline with a generation_spend_cap_usd you would accept spending in full.

The loop

From the Agent Completions docs: create with POST /v1/agent/completions and a required cap, poll the receipt's status_url, and cancel with a POST to the cancel URL. Set SUME_API_KEY in your environment; the key needs agent_completions:write.

import json, os, time, urllib.request

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

def call(url, body=None, method="GET"):
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request(url, data=data, method=method, headers=H)
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.load(r)["data"]

def run_with_deadline(instruction, cap_usd, deadline_s=600):
    run = call("https://api.sume.com/v1/agent/completions",
               {"instruction": instruction,
                "generation_spend_cap_usd": cap_usd}, "POST")
    stop = time.monotonic() + deadline_s
    delay = 3
    while time.monotonic() < stop:
        st = call(run["status_url"])
        if st.get("next_action") != "poll_status":
            return call("https://api.sume.com/v1/agent-runs/" + run["id"])
        time.sleep(delay)
        delay = min(delay * 1.5, 20)
    call(run["cancel_url"], {}, "POST")
    return {"id": run["id"], "status": "canceled_by_caller"}

Why it is shaped this way

  • The docs say to follow the receipt's URLs rather than building them, so the loop uses status_url and cancel_url as returned.
  • Backoff grows to 20 seconds so a long run does not hammer the status route.
  • On a terminal status the final read is the full receipt, where output, artifacts and usage live.
  • No Idempotency-Key is sent here for brevity. In production add one so a network retry of the create call returns the same receipt.

What a deadline can and cannot do

A cancel request is a request. A run that has already reached a terminal status has nothing left to cancel. After cancelling, read the run once more and trust the status it reports, not the one your loop assumed.

Also set the deadline above the typical duration of the work. A video task that normally needs ten minutes will always be cut by a 60 second deadline, and the cut run may already have spent part of its cap. Pick the cap first, then a deadline that leaves headroom.

Statuses and what to do (read 2026-10-04)
StatusMeaningLoop action
queued, processingStill runningKeep polling with backoff
completedOutput availableRead output and usage
failedRun ended in errorRead error, do not blindly retry
canceledStopped by cancelRecord it and move on

Where this fits

If you would rather not poll at all, pass a public HTTPS communication.webhook_url and treat polling as the backup; see Run webhooks. Jobs that Sume agents start internally are separate from your run id, and the jobs page covers reading those. Keep keys and signed URLs out of logs, per Safe automation.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume