Cancel a Sume schedule run: actions:write, idempotent, 200 on terminal
POST /v1/action-runs/{run_id}/cancel needs actions:write and is idempotent: canceling a finished run returns its terminal receipt. What happens to spend.

To stop a Sume schedule run, send POST /v1/action-runs/{run_id}/cancel with a key that has actions:write. The call is idempotent, and canceling a run that already reached a terminal state returns that terminal receipt with 200, so a retry is safe. The receipt's cancel_url gives the exact path.
From Runs and results, Run a schedule via API and Run webhooks, read on 2026-10-03.
The call and what it returns
The receipt of an accepted run includes cancelable and cancel_url. Check cancelable before calling, though a call on a terminal run is harmless. A canceled run ends with status canceled, spelled with one l.
Cancel needs actions:write, while reading a run needs only actions:read. A monitoring key can therefore hold read access and never stop work.
| Situation | Result |
|---|---|
| Run is queued or processing | Run is canceled; status becomes canceled |
| Run is already terminal | 200 with that terminal receipt |
| Key has only actions:read | Cannot cancel; cancel needs actions:write |
| Cancel called twice | Same outcome both times |
Webhooks and cancellation
Run webhooks send one terminal event per run family, but a canceled run does not deliver a webhook. After you POST the cancel, trust the cancel response and poll status_url until payload.status is canceled; do not wait for a POST. A skipped run never delivers a webhook either.
Keep this in the state machine: a run you canceled has no inbound event to close it, so your own code must mark it closed.
Spend after a cancel
Every run carries a generation spend cap, $1.00 by default when unset, and the cap bounds what the run can spend on generation. The cap can be lowered per run but never raised. Whether a canceled run spent anything is a question for usage data, not an assumption: the MCP usage_get tool takes a run_id and reports what the wallet debited, with holds and refunds shown separately from spend.
If your automation cancels runs on a timer, log the run id, the cancel response status and the usage read afterwards. That gives an audit trail without relying on any guess about cost.
Service-account keys
Service-account keys cannot create schedule runs and fail with 403 insufficient_scope. Use a regular key with actions:write for the component that starts and stops runs, and keep the key in an environment variable, never in the repository or in a prompt.
A cancel routine you can run unattended
Because cancel is idempotent, an unattended routine can call it without checking state first. Send the cancel, read the returned receipt, and branch on its status: canceled means the stop worked, and any other terminal status means the run finished before the cancel arrived.
Do not retry a cancel in a tight loop. One call is enough, and repeating it returns the same receipt. If the status is still processing after the call, poll status_url at a sensible interval instead.
Sources
Related posts
More in Developers
- Cancel a Sume video job twice: the second call returns the same job
POST /v1/jobs/{id}/cancel is idempotent for a canceled job and returns 409 once generation has started. How to write a cancel that is safe to retry.
- Caption a folder of videos in Python: thread pool, one key per file
Submit caption jobs from a Python thread pool without double billing: one Idempotency-Key per file, poll with next_poll_after_seconds, and the two 429s.
- Caption design colors: hex, rgb, rgba, transparent only, else 400
Sume's caption design.colors accepts hex, rgb(), rgba() or transparent and rejects other CSS with a 400. Fields, examples and a safe validator.
- Cheapest paid call per Sume endpoint: a CI smoke-test budget
A smoke run touching 14 paid Sume endpoints at their catalog minimum costs about $0.60. Per-endpoint table plus 20, 30 or 528 runs a month.
Written by Sume