Daily Sume schedule: skipped and canceled runs send no webhook
A Sume run webhook fires only on completed or failed. For a daily schedule, a skipped or canceled run is silent, so add a list-runs check each morning.

If you only listen for webhooks on a daily Sume schedule, you will not hear about a skipped or canceled run. Sume sends one signed POST when a run completes or fails. A skipped run never starts work and a canceled run has its own API path, and the docs say neither delivers a webhook. The fix is a small check that lists recent runs after the time you expect the run to have finished.
Which outcomes produce a POST
The run webhook page states the rule per outcome. The table below is read from the Sume docs on 2026-10-09 and covers Action runs, which is what a schedule creates.
The same holds for a Format run, except that its overlap default is allow rather than skip, so a Format run does not skip on its own. The schedule default is the one that surprises people who move from Formats.
| Run status | Webhook sent | How you learn about it |
|---|---|---|
| completed | Yes, status OK | POST, or poll status_url |
| failed | Yes, status ERROR | POST, or poll status_url |
| canceled | No | Poll status_url until canceled |
| skipped | No | Read status on the create response, or list runs |
Why a daily schedule can skip
Only one run of a schedule is active at a time. The default for on_active_run on Action runs is skip. If yesterday's run is still processing when today's cron fires, the new run is recorded with status skipped and skip_reason previous_run_active, and the receipt tells a poller to retry later through next_action.
That is a valid result, not an error, so the HTTP status is 200 and no alert fires. A schedule whose runs routinely take longer than their cadence will skip quietly for days if nothing watches for it.
A morning check against the list endpoint
GET /v1/actions/{action_id}/runs returns recent runs newest first, with limit from 1 to 100. The script below reads the latest five and decides if a run in the last 26 hours completed. It needs a key with actions:read and prints one line you can route to your pager. It does not read the schedule's instructions, which the public shape omits.
import json, os, urllib.request
from datetime import datetime, timedelta, timezone
KEY = os.environ["SUME_API_KEY"]
ACTION = os.environ["ACTION_ID"]
URL = f"https://api.sume.com/v1/actions/{ACTION}/runs?limit=5"
def latest_runs():
req = urllib.request.Request(URL, headers={"Authorization": f"Bearer {KEY}"})
with urllib.request.urlopen(req, timeout=20) as r:
return json.load(r)["data"]
def check(max_age_hours=26):
cutoff = datetime.now(timezone.utc) - timedelta(hours=max_age_hours)
for run in latest_runs():
made = datetime.fromisoformat(run["created_at"].replace("Z", "+00:00"))
if made < cutoff:
break
if run["status"] == "completed":
return "ok"
if run["status"] in ("skipped", "canceled", "failed"):
return f"alert: {run['status']} {run.get('skip_reason')}"
return "alert: no run in window"
print(check())Choosing the window
Pick the window from the schedule's cadence plus its slowest normal run, not from the cron expression alone. The 26 hours above leaves two hours of slack on a 24-hour cadence. A skipped run inside the window is reported as an alert rather than ignored, so a skip still wakes someone even though no webhook told you.
Keep the webhook as well. It is the fast path for completed and failed runs, and dedupe on the envelope request_id, which equals run_id and stays stable across retries. The check above only covers the cases a POST cannot.
The script is read-only and safe to run from a cron job of your own. Send its output to the same channel as your webhook alerts, so a quiet morning means every check returned ok. If you need history for a post-mortem, page the list with next_cursor until has_more is false, since the cursor is opaque and must be sent back unchanged.
Sources
Related posts
More in Agents
- Five gates between a Sume MCP write session and your wallet
A hosted Sume MCP write session has five gates before money moves: scope, idempotency_key, dry_run, an optional max_spend_usd, and wallet admission.
- Sume schedule run: communication.mode webhook alone sends nothing
On a Sume schedule run, communication.mode is descriptive only. Delivery starts when communication.webhook_url is set, an HTTPS URL up to 2,048 characters.
- script_run limits: timeout 5-55 s, max_calls and max_paid_calls
Sume's script_run runs a short script of MCP tool calls with a 5 to 55 second timeout and caps on calls and paid calls. What it returns and cannot call.
- Canceled and skipped Sume agent runs send no webhook: what to poll
A Sume run webhook fires only when a run completes or fails. Canceled and skipped runs send nothing, so your scheduler must read status_url for them.
Written by Sume