Agent Completions webhook payload null: fetch result_url (1 MiB)

A Sume agent.run webhook over 1 MiB arrives with payload null and an error carrying result_url, while status still says OK. Verify, then fetch the receipt.

4 min readSume
All posts

If your Agent Completions webhook shows payload: null, the run did not fail. Sume does not deliver a receipt larger than 1 MiB inline. It sends the envelope with payload set to null and an error object whose code is payload_too_large and which carries a result_url. The envelope status still reports the real outcome, so a successful run that was too big to ship is still OK. This is from Sume's run webhooks documentation, read on 2026-10-04.

What does the envelope contain?

Fields from Sume's Run webhooks page, read 2026-10-04.
FieldOn an oversized delivery
eventagent.run.terminal
status / outcomethe real result, such as OK and ok
payloadnull
error.codepayload_too_large
error.result_urlwhere to fetch the receipt
request_idthe run id, stable across retries

How should the handler work?

Verify the signature on the raw body first. Sume signs HMAC-SHA256 over the timestamp, a dot, and the raw body, and sends x-sume-webhook-timestamp and x-sume-webhook-signature as sume-v1=<hex>. Reject timestamps outside a window; five minutes is the suggested default. Then record the event and answer 2xx quickly, since each attempt has a 10-second timeout and up to 10 attempts are made. Dedupe on request_id.

After the check, branch on payload. When it is null and the code is payload_too_large, fetch the receipt from result_url with an API key that has agent_completions:read.

import hashlib, hmac, time

def verify(raw: bytes, ts: str, sig: str, secret: str) -> bool:
    if not secret:
        raise ValueError('empty webhook secret')
    if abs(time.time() - int(ts)) > 300:
        return False
    mac = hmac.new(secret.encode(), ts.encode() + b'.' + raw, hashlib.sha256)
    return hmac.compare_digest('sume-v1=' + mac.hexdigest(), sig)

def receipt_url(event: dict):
    if event.get('payload') is None:
        err = event.get('error') or {}
        if err.get('code') == 'payload_too_large':
            return err['result_url']
    return None

Where do I get the secret?

The signing secret is on the Webhooks tab of the dashboard, or from GET /v1/webhooks/signing-secret with a key that has account:read. Sume derives it for your workspace, and a different workspace's secret cannot verify your delivery. The verifier above refuses an empty secret on purpose, so a missing environment variable fails loudly instead of accepting everything.

What if delivery fails?

A failed delivery does not change the run. After ten rejected attempts the delivery is exhausted, and the run stays complete. Fetch it from result_url, as the Run webhooks page says, or poll the status_url. A run starts with POST /v1/agent/completions, which needs generation_spend_cap_usd; see Agent Completions. Use the Jobs and results page for generation-job events, which are a separate surface.

What should I log?

Log the request_id, the outcome, and whether payload was null. Do not log the signing secret or the raw body in full, because a run receipt can carry long text. When the body is oversized, the receipt is still waiting at result_url, so a log line with the run id is enough to find it again. Keep the handler fast: record, answer 2xx, and fetch afterwards.

Sources

Related posts

More in Agents

All Agents posts

Written by Sume