Does a canceled Sume job send a webhook? Jobs do, runs do not

A canceled generation job delivers job.canceled with status ERROR, but a canceled or skipped Format, Action or Agent run sends no webhook. Handle both paths.

5 min readSume
All posts

It depends on which surface you called. A generation job (POST /v1/models/... or a model route such as /v1/avatar-1.0/generate) sends job.canceled when it reaches the canceled state. A Format, Action or Agent Completion run never sends a webhook when canceled or skipped; its terminal event only fires on completed or failed. If your integration is webhook-only, run cancellation is the one path where nothing arrives.

Source pages: Webhooks, Run webhooks and Jobs and results, read 2026-10-02.

Which events does each surface send?

Both surfaces sign deliveries with the same sume-v1 scheme, so one verifier covers both. The event sets do not overlap.

Terminal webhook events by surface, read 2026-10-02
SurfaceEventsCanceledSkipped
Generation jobjob.completed, job.failed, job.canceledDelivers job.canceled with status ERROR and an error objectNot applicable
Format runformat.run.terminalNo webhookNo webhook
Action runaction.run.terminalNo webhookNo webhook
Agent Completion runagent.run.terminalNo webhookNo webhook

When can a job be canceled at all?

Cancellation succeeds only before generation work starts. Once generation has started, the API returns 409 job_generation_already_started with details.cancelable: false, and the job runs to completion. So job.canceled realistically follows a cancel of a job that was still queued. Canceling a job that is already canceled is idempotent and returns the same canceled job.

Runs behave differently. POST .../cancel on a run returns the current receipt either way, and cancel_effect says canceled when this call stopped a run in flight or no_op when it had already finished. Generation the run completed before the cancel is still billed.

How do I write a handler that does not hang on cancel?

Route on the event field, and treat a run you canceled as resolved the moment the cancel call returns. Do not leave a pending record waiting for a POST that cannot come. After you call cancel on a run, trust the cancel response, or poll status_url until payload.status is canceled.

For jobs, branch on all three events. job.failed and job.canceled both carry status: "ERROR" and an error object, so read the event name, not just the status, if you need to tell a failure from a cancel.

function handle(event: { event: string; job_id?: string; run_id?: string }) {
  switch (event.event) {
    case "job.completed":
      return markDone(event.job_id!);
    case "job.failed":
    case "job.canceled":
      return markEnded(event.job_id!, event.event);
    case "format.run.terminal":
      return markRun(event.run_id!); // never sent for canceled or skipped
    default:
      return; // unknown event: answer 204, not 500
  }
}

What about skipped runs?

A skipped run is recorded terminal immediately, without ever starting work, so there is no completion to announce. The create response already told you. Format defaults to on_active_run: "allow" while Action defaults to skip, so Action integrations meet this more often. Read status on the create response instead of waiting.

Net rule: for jobs, expect three events; for runs, expect one event for success or failure and use the API response for cancel and skip.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume