Sume webhook payload is null: receipt over 1 MiB, fetch result_url
A Sume format.run.terminal webhook with payload null means the receipt was over 1 MiB. Read error.result_url and fetch the receipt instead of failing the run.

A webhook whose payload is null is not a failed run. Sume sets payload to null only when the run receipt was more than 1 MiB, and in that case error.code is payload_too_large and error.result_url gives the address to fetch the full receipt. Your handler should fetch that URL rather than treat the empty payload as missing data (Sume docs: Format runs, read 2026-10-06).
This tends to happen when a run produces a big structured output or many artifacts, for example a season-length manifest.
Read the outcome first
The event carries an outcome of ok, degraded or error and an error object that is null on ok. A null payload together with outcome ok and an error code of payload_too_large is the large-receipt case. A null payload with a different error code is a real problem, so branch on the code, not on the null.
The payload, when present, is byte-identical to the data returned by GET /v1/format-runs/{run_id}. That is deliberate: one handler can serve both the webhook and a poll, because they return the same object.
A handler that copes with both
Verify the signature first. The webhook is signed with sume-v1 HMAC-SHA256 over the timestamp, a dot and the raw body, inside a five-minute window, and the verifier must refuse an empty secret. Then choose between the inline payload and a fetch.
import os, requests
API_KEY = os.environ["SUME_API_KEY"]
def receipt_from_event(event):
if event.get("payload") is not None:
return event["payload"]
err = event.get("error") or {}
if err.get("code") != "payload_too_large":
raise RuntimeError("no payload: " + str(err.get("code")))
r = requests.get(err["result_url"], timeout=60,
headers={"Authorization": "Bearer " + API_KEY})
r.raise_for_status()
return r.json()["data"]
event = {"payload": None,
"error": {"code": "payload_too_large",
"result_url": "https://api.sume.com/v1/format-runs/RUN_ID/result"}}
print("would fetch:", event["error"]["result_url"])Keep deliveries idempotent
Sume retries a failed delivery up to 10 times, starting at 30 seconds and doubling, capped at one hour. Dedupe on request_id and order by created_at. If your fetch of result_url fails, return a non-2xx and let the retry come back, or schedule your own fetch later. A run also keeps a redeliver endpoint you can POST to once you have fixed your receiver.
Do not log the fetched receipt wholesale. It can hold signed media URLs. Log the run id and the outcome.
| payload | error.code | What to do |
|---|---|---|
| Object | null | Use the payload |
| null | payload_too_large | Fetch error.result_url |
| null | Anything else | Treat as a real error |
Prevent it
Keep receipts small. Return URLs to files instead of file contents, bound arrays with maxItems, and avoid asking the run to paste large text into the output. A receipt under 1 MiB arrives inline, which makes your handler simpler.
Test the large-receipt path
You will rarely see a payload_too_large event in normal work, which is exactly why the branch breaks first when it finally happens. Write a test that feeds your handler an event with payload null and the error code set, and confirm it fetches the result and processes it the same as an inline one.
Also test the failure of the fetch itself. If the request to result_url times out, your handler should return a non-2xx so Sume retries the delivery, rather than swallowing the event and leaving the run unrecorded. The retry schedule gives you ten attempts, which is plenty of time to recover from a short outage.
Sources
Related posts
More in Formats
- Why did my Format run do that? Read the first message of its thread
A Format run's first thread message is the text the agent received: Format pointer, your instruction, unattended note and input file path. Read it first.
- Ready-made Formats for product video: the Sume Format catalog
Sume ships ready-made Formats for product and UGC-style video and images, each callable from your backend with one HTTP request at the reserved sume handle.
- What is a Sume Format? Turn an agent thread into one API call
A Sume Format is a saved video recipe your backend calls by handle and slug. One POST runs it in a fresh sandbox and returns media plus optional typed JSON.
- How to embed AI video generation in your product with Sume Formats
To embed AI video generation, your server holds one Sume API key and runs a Format per customer, with a derived Idempotency-Key, spend cap, and webhook.
Written by Sume