Sume webhooks: 10 attempts 30 seconds apart for a video receiver

A Sume job webhook is tried up to 10 times, 30 seconds apart by default, with a 10 s timeout each. What that means for a video receiver, plus a Python verifier.

5 min readSume
All posts

A Sume job webhook is attempted up to 10 times in total, with a fixed 30-second delay between attempts by default and a 10-second timeout on each attempt. If your receiver is down for a deploy, that is roughly 4.5 minutes of automatic retries (nine gaps of 30 seconds) before the delivery is exhausted. The job itself still reaches its terminal state either way.

That matters now because teams that polled the retired Sora Videos API are choosing between polling and callbacks for the replacement. OpenAI removed the API on 2026-09-24 per the Pondero report. A new receiver has to be built, so build it to these numbers.

The delivery rules

Sume job webhook delivery behavior (read 2026-10-07)
RuleValue
AttemptsUp to 10 in total
SpacingFixed, 30 s by default, not exponential
Timeout per attempt10 s
SuccessAny 2xx after you store the event durably
Eventsjob.completed, job.failed, job.canceled (terminal only)
Idempotency key on your sidejob_id

Design consequences

A 10-second timeout means the handler must acknowledge fast. Store the event, answer 2xx, and download the MP4 in a separate worker. A handler that fetches a video inside the request burns attempts on its own slowness.

The docs say to treat delivery as an optimization and not the only recovery path. After ten refused attempts the job is still done; you simply were not told. Keep a polling sweep that reads open jobs by id.

On /v1/videos, the field is callback_url and it must be HTTPS. The payload is Sume's standard job envelope, not the OpenRouter video.generation.* shape, so a receiver written for OpenRouter events needs a new parser.

A verifier that refuses an empty secret

Sume signs <timestamp>.<raw_body> with HMAC SHA-256 and sends x-sume-webhook-timestamp and x-sume-webhook-signature: sume-v1=<hex>. During a secret rotation the header holds several comma-separated entries, so accept any match. The docs suggest a five-minute replay window.

import hashlib, hmac, os, time

def verify(raw: bytes, ts: str, header: str, secret: str, tol: int = 300) -> bool:
    if not secret:
        raise ValueError("signing secret is empty")
    try:
        t = int(ts)
    except ValueError:
        return False
    if abs(time.time() - t) > tol:
        return False
    digest = hmac.new(secret.encode(), f"{t}.".encode() + raw, hashlib.sha256).hexdigest()
    want = f"sume-v1={digest}"
    ok = False
    for entry in header.split(","):
        if hmac.compare_digest(entry.strip(), want):
            ok = True
    return ok

def main() -> None:
    secret = os.environ["SUME_COM_WEBHOOK_SIGNING_SECRET"]
    body = b'{"event":"job.completed"}'
    ts = str(int(time.time()))
    sig = hmac.new(secret.encode(), ts.encode() + b"." + body, hashlib.sha256).hexdigest()
    print(verify(body, ts, "sume-v1=" + sig, secret))

if __name__ == "__main__":
    main()

When the window is missed

If every attempt fails, POST /v1/jobs/{job_id}/webhook/redeliver re-sends the real terminal event with a fresh timestamp and signature, and it does not use up one of the automatic ten. That is the repair path after an outage; the redelivery post covers it.

Sizing the receiver

A fixed 30-second gap means a burst of finished jobs produces a burst of deliveries, and a failing endpoint is hit again by every undelivered job every 30 seconds. If you submit 24 jobs on a Pro workspace and your receiver is down when they finish, expect up to 24 retries each half minute until it recovers. Make the handler cheap enough to take that: verify, insert into a table keyed by job_id, return 200.

Do the slow work later. A worker that reads the table can fetch the artifact URL from the job result, copy the MP4 to your own storage, and update your application. Sume mirrors generated outputs to Sume-owned media URLs, and the docs say to store the Sume URL rather than a raw provider one.

Keep polling as the backstop. A sweep every few minutes over jobs that are still open in your table catches deliveries that never arrive for any reason, including a delivery that was exhausted before you noticed.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume