Sume run webhook says OK but output is null: handle degraded
A Sume run can complete, bill you, and still have output null. Branch on outcome, not status, and read artifacts and output_error before you retry.

When a Sume run webhook has status: OK but output is null, the run completed and billed, and Sume could not project its media onto your output_schema. The envelope marks this with outcome: "degraded". Branch on outcome, and read artifacts and output_error instead of retrying blindly.
Three outcomes, two statuses
The run webhooks page says status is binary: OK when the run completed and ERROR when it failed. outcome carries the finer answer to the question "did I get usable output".
| outcome | status | What you hold | Action |
|---|---|---|---|
| ok | OK | Structured output in payload.output | Ship it. |
| degraded | OK | Real artifacts, output null, output_error set | Review the artifacts; fix the schema. |
| error | ERROR | Full receipt, often with artifacts and output_error | Retry or alert. |
Why degraded exists
A run can generate real media, bill for it, and still fail to project that media into your schema. The docs name a typical cause: a schema that asks for a field the run never produces. In that case a handler that checks only status sees OK and ships a null. It cannot tell ok from degraded.
A handler
The shape below follows the switch in the docs. It never treats a null output as success.
function handleRun(event) {
switch (event.outcome) {
case "ok":
return ship(event.payload.output);
case "degraded":
// Real artifacts, no structured output. Check the schema first.
return review(event.payload.artifacts, event.payload.output_error);
case "error":
return retryOrAlert(event.payload?.error ?? event.error);
default:
throw new Error("unknown outcome: " + event.outcome);
}
}Do not retry a degraded run as if it failed
A degraded run already spent generation budget. A retry with the same schema repeats the mismatch and spends again. Fix the schema or the instruction, and if you do re-run, send a new Idempotency-Key, because the same key with a different payload returns 409 idempotency_conflict on Agent Completions.
Whatever model plans the run
Small fast models, such as Claude Haiku 5.5 released on 2026-10-07 and positioned by Anthropic for high-volume work and subagents, are attractive for unattended runs. Whatever model plans the run, Sume projects the output after the run completes and reports failure in output_error. The branch above works regardless of the model, and it is the place to log a schema that no run can satisfy.
What to log
When you hit the degraded branch, log the run id, the outcome, the output_error.code, and the number of artifacts. Do not log signed URLs or raw private media URLs; the safe-automation guidance lists them as unsafe. For Agent Completions the media URLs in a completed receipt are durable media.sume.com HTTPS URLs, so you can store the reference and review the files later. A short log line with those four fields is enough for someone to decide whether the fix is in the schema, in the instruction, or in the request.
One more case to design for is an oversized receipt. Sume cannot deliver a receipt of more than 1 MiB inline, so it sends the envelope with payload: null and an error code payload_too_large that includes a result_url. In that case status still reports the real outcome. If your handler reads event.payload.output without a null check, it will throw there too. Fetch the receipt from result_url and run it through the same branch.
Sources
Related posts
More in Developers
- Sume submit: request_id is the job id you poll (curl, jq)
After an async submit, data.request_id is the id for GET /v1/jobs/:id. error.request_id is for support tickets. A curl and jq check.
- Sume TTS word timestamps to caption cues for a narrated 60-second clip
Ask Sume TTS for timestamps.words, group them into cues and send them to video-captions so no recognition runs. About 25 cents for a 60-second narration.
- Sume TypeScript SDK waitForJob: 20-minute timeout, job keeps billing
How @sume-com/sdk waitForJob polls a generation job, what SumeJobTimeoutError means, and why a client timeout does not cancel or refund the job.
- Webhook endpoint down: redeliver a Sume video job after the retries
Sume retries a job webhook 10 times, 30 seconds apart. If your receiver was down longer, POST /v1/jobs/{id}/webhook/redeliver re-sends the terminal payload.
Written by Sume