Python Workers ASGI: a FastAPI Sume webhook receiver
Python Workers now accept ASGI frameworks. A FastAPI Sume receiver keeps the same rules there: raw body, constant-time HMAC compare, replay window, fast 2xx.

Yes, you can run a FastAPI Sume receiver on Cloudflare Python Workers: since September 2, 2026 any WSGI or ASGI framework can be used there, through wsgi or asgi from the workers module. The verification logic does not change. Read the raw body, check sume-v1 in constant time, enforce the replay window, return 2xx fast.
Cloudflare facts are from two changelog entries; Sume facts from the Format cookbook and Run webhooks, read 2026-09-30. The changelog snippets I read do not show wiring code, so follow Cloudflare's page for the adapter call.
What changed on Python Workers?
Frameworks following WSGI or ASGI can be used in Python Workers: choose wsgi for Django or Flask and asgi for ASGI frameworks such as FastAPI. A separate September 8, 2026 change makes Python 3.14 the default for new Python Workers with compatibility date 2026-09-08 or later.
What stays the same in the receiver?
| Rule | Sume docs |
|---|---|
| Body | Read the raw body before any JSON parsing |
| Signed string | <timestamp>.<raw_body>, HMAC SHA-256, hex |
| Header | x-sume-webhook-signature: sume-v1=<hex_signature> |
| Compare | hmac.compare_digest |
| Replay window | Cookbook uses TOLERANCE_SECONDS = 300 |
| Response | 2xx quickly, after durably recording the event |
What changes on a Worker?
Where the secret comes from. The cookbook reads os.environ["SUME_COM_WEBHOOK_SIGNING_SECRET"] at import time; on a Worker, supply the secret through whatever binding mechanism Cloudflare documents, and keep the name SUME_COM_WEBHOOK_SIGNING_SECRET. Refuse to verify when it is empty. The function below is the cookbook's check with that guard added.
import hashlib
import hmac
import time
def verify(secret: bytes, raw: bytes, timestamp: str | None, signature: str | None) -> bool:
if not secret or not timestamp or not signature:
return False
try:
ts = int(timestamp)
except ValueError:
return False
if abs(time.time() - ts) > 300:
return False
digest = hmac.new(secret, f"{ts}.".encode() + raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sume-v1={digest}", signature)How should the handler respond?
Verify, record the event, return 2xx. Each attempt times out at 10s, and a slow endpoint burns the attempt budget and gets retried. Dedupe on request_id for runs or job_id for jobs. The server-side equivalent on regular hosts is in Python webhook receiver: FastAPI and Django. During a secret rotation the header can carry several comma-separated sume-v1= entries, so split on commas in production; secret rotation shows how.
Sources
Related posts
More in Developers
- Ramp up API traffic gradually: pace Sume submits to the write budget
OpenAI now returns a slow_down 429 when traffic grows too fast. On Sume, pace paid submits to your plan's write budget and read ratelimit-remaining.
- Remove filler words from a talking video with an API
Sume has no one-call filler remover. Transcribe with video inspect for word timings, then cut the clean ranges with video trim at $0.02 per job.
- Remove filler words from a video by API: cut at word timestamps
Descript's API lists Remove Filler Words as an Underlord edit. the Sume docs list no such op; here is how to cut um and uh yourself from words[] and a Timeline.
- OpenAI response_format json_schema on a Sume scheduled run
Sume accepts an OpenAI-shaped response_format as an alias for output_schema on schedule runs. Sending both returns 400, and the schema must be strict.
Written by Sume