Supabase middleware pipeline: verify a Sume webhook first
Put Sume's verifyWebhook in the first step of a Supabase middleware pipeline: return 401 on a bad signature and handlers only see verified events.

Make signature verification the first step of the pipeline. Read the raw body, call Sume's verifyWebhook, and return a 401 Response when it fails. Supabase's changelog says any middleware can return a Response and stop the chain, so later steps and your handler only ever see events that passed.
Supabase's side is from its changelog entry for Supabase Middleware 1.0 (read 2026-09-30). Sume's side is from Verifying webhooks. I did not read the package's API reference, so the code below is a plain Fetch-style step you wrap with defineMiddleware yourself.
What does the Supabase changelog say about the pipeline?
The changelog says @supabase/middleware 1.0.0 is on npm and JSR. defineMiddleware writes one piece of per-request logic, pipeline([...], handler) runs several in order, and the engine runs wherever fetch runs, including Supabase Edge Functions, Vercel Functions, Cloudflare Workers, Deno, Bun and Node 22 or newer. A middleware can read and change the response on the way out, or return one and stop the chain.
What must the verify step do?
| Rule | What the docs say |
|---|---|
| Body | Read it raw with request.text() before any JSON.parse |
| Secret | SUME_COM_WEBHOOK_SIGNING_SECRET |
| Failed check | Return 401 with a short body |
| Routing | One verifier covers run and job events; route on event |
| Unknown event | Return 204, not a 500 and a retry storm |
What does the step look like?
The step returns the body to continue, or a Response to stop. The verified body goes onward as text so the next step parses it once.
import { verifyWebhook } from "@sume-com/sdk";
export async function verifySume(
request: Request,
secret: string,
): Promise<{ body: string } | Response> {
const body = await request.text(); // raw, before any JSON.parse
const ok = await verifyWebhook({ body, headers: request.headers, secret });
if (!ok) return new Response("bad signature", { status: 401 });
return { body };
}
export function routeSume(body: string): Response {
const event = JSON.parse(body);
switch (event.event) {
case "job.completed":
case "job.failed":
return new Response(null, { status: 204 });
default:
return new Response(null, { status: 204 }); // unknown event
}
}How do I wire it into the pipeline?
Wrap verifySume in defineMiddleware so it contributes the verified body to the shared context, and place it first. Order matters: Supabase says a middleware placed before its prerequisite fails to compile. Put any other steps after it.
What should the handler do after verification?
Dedupe on job_id for job events (the docs dedupe run events on request_id) and return a fast 2xx, then do the work. Sume makes up to 10 delivery attempts in total and a failed delivery is not a failed job, so also keep status polling as a backup. Supabase edge function video webhook covers the handler side.
Sources
Related posts
More in Developers
- Telegram sendLivePhoto: 10 s, 10 MB and trimming a clip
Telegram's sendLivePhoto video must be 10 seconds or less and 10 MB or smaller. Sume's video trim sets length and frame size; it has no byte-size target.
- Temporal activity cancel: Sume job_generation_already_started
When Temporal cancels an activity, a Sume job may already be generating. Cancel returns 409 job_generation_already_started; let it finish and keep the artifact.
- Temporal BlobSizeLimitError: pass the Sume artifact URL, not bytes
Temporal Cloud allows 2 MB per payload. Pass the Sume job_id and media.sume.com artifact URL through activities and signals, never video bytes.
- Text to dialogue API: continuity between requests on Sume
Sume's TTS has no previous_text field. Keep a line seamless with one longer transcript, or join separate takes with a gapless Timeline audio concat.
Written by Sume