Supabase Edge Function webhook for Sume: JWT off, HMAC on
Sume's webhook POST carries no Supabase JWT, so deploy the Edge Function with verify_jwt = false and check Sume's HMAC signature on every delivery.

A Supabase Edge Function can receive Sume video webhooks once Supabase's JWT check is off for it: Edge Functions require a valid JWT by default, and Sume's POST carries none. Set verify_jwt = false for that function in supabase/config.toml, and authenticate every delivery with Sume's HMAC signature instead, using verifyWebhook imported from npm:@sume-com/sdk.
Supabase facts come from its Function Configuration, Handling Stripe Webhooks, Background Tasks, Limits, and related pages under Sources; Sume facts come from Run webhooks, Verifying webhooks, and Runs and results. All were read on 2026-09-27. There is no Sume connector for Supabase: this is a Deno function that Sume POSTs to. The general webhook contract is in Signed webhooks for video runs.
Why does the Edge Function reject Sume's POST?
By default, Edge Functions require a valid JWT in the authorization header, and Sume sends no Supabase token. Supabase documents the fix for this case, with Stripe webhooks as its example: a per-function [functions.sume-webhook] entry in supabase/config.toml with verify_jwt = false. When serving locally, the --no-verify-jwt flag on supabase functions serve does the same.
Supabase warns that this lets anyone invoke the function without a valid JWT. So the Sume signature check is not optional here: it is what stands between the public URL and your database. Supabase's own Stripe webhook function follows the same pattern, wrapping its handler in withSupabase({ auth: 'none' }), where the starter template from supabase functions new expects a publishable or secret key.
What does the Edge Function look like?
Edge Functions import npm packages with npm: specifiers, and @sume-com/sdk runs on Deno. Read the body with await req.text() before any JSON.parse, as the Stripe example also does, because verification needs the raw bytes. verifyWebhook returns false rather than throwing.
// supabase/functions/sume-webhook/index.ts
import { withSupabase } from "npm:@supabase/server@^1";
import { verifyWebhook } from "npm:@sume-com/sdk@0.2.0";
// Sume signs every delivery, so deploy with verify_jwt = false.
export default {
fetch: withSupabase({ auth: "none" }, async (req) => {
const body = await req.text(); // raw, before JSON.parse
const ok = await verifyWebhook({
body,
headers: req.headers,
secret: Deno.env.get("SUME_COM_WEBHOOK_SIGNING_SECRET")!,
});
if (!ok) return new Response("bad signature", { status: 401 });
const event = JSON.parse(body);
if (event.event !== "format.run.terminal") return new Response(null, { status: 204 });
const fresh = await recordOnce(event.request_id, event); // insert-or-ignore
if (fresh) EdgeRuntime.waitUntil(notifyUser(event)); // runs after the 204
return new Response(null, { status: 204 });
}),
};How long can the function keep working after it answers?
EdgeRuntime.waitUntil(promise) keeps the instance running until the promise completes, without blocking the response. It is still capped by the wall-clock, CPU, and memory limits, so it suits a notification or a row update, not waiting on more generation. In local tests, the CLI terminates instances after each request, which stops background tasks early; Supabase's fix is policy = "per_worker" under [edge_runtime] in supabase/config.toml.
| Limit | Value |
|---|---|
| Sume delivery attempt | 10 s per attempt, up to 10 attempts |
| Edge Function wall clock | 150 s on Free, 400 s on paid plans |
| CPU time | 2 s per request, not counting async I/O |
| Request idle timeout | 150 s; without a response by then, 504 Gateway Timeout |
| Memory | 256MB |
| Sume long-form host video run | Typically 15 to 30 minutes |
Where do the secret and the URL come from?
Set both up once, before the first run:
- Copy the signing secret from the Webhooks tab of the Sume dashboard, or read it from
GET /v1/webhooks/signing-secretwith a key carryingaccount:read. - Store it with
supabase secrets set SUME_COM_WEBHOOK_SIGNING_SECRET=…. Secrets are available immediately, with no redeploy, and a name cannot start withSUPABASE_. The function reads it withDeno.env.get. - Deploy with
supabase functions deploy sume-webhook. The function then runs athttps://[YOUR_PROJECT_ID].supabase.co/functions/v1/sume-webhook, a public HTTPS URL; pass it ascommunication.webhook_urlwhen you start a run. - The local server at
http://localhost:54321/functions/v1/…cannot receive deliveries, because Sume rejects localhost and non-HTTPS URLs. See Test Sume webhooks on localhost.
What should the function store?
Key the stored row on request_id, which repeats on every retry, and keep payload.primary_output_url with it. Format run media URLs on media.sume.com are durable, but public to anyone holding the URL, so proxy or copy them if your app needs per-user access control. A receipt over 1 MiB arrives with payload: null; fetch it from error.result_url with your API key, stored as another secret. Signed webhooks for video runs covers the rest of the delivery contract.
Sources
- Run webhooks
- Verifying webhooks
- Runs and results
- TypeScript SDK
- Formats
- Supabase: Function Configuration (read 2026-09-27)
- Supabase: Handling Stripe Webhooks (read 2026-09-27)
- Supabase: Managing dependencies (read 2026-09-27)
- Supabase: Background Tasks (read 2026-09-27)
- Supabase: Limits (read 2026-09-27)
- Supabase: Environment variables (read 2026-09-27)
- Supabase: Getting Started with Edge Functions (read 2026-09-27)
Related posts
More in Integrations
- Telegram bot to generate video: a Sume job, then sendVideo
A Telegram bot can turn /video into a Sume job, reply at once, then call sendVideo with the artifact URL when Sume's signed webhook arrives.
- Text-to-video API in Python: submit, poll, and download
Call Sume's text-to-video API from Python with Requests: POST /v1/videos, poll with timeouts, then stream the MP4 the content route redirects to.
- Trigger.dev wait for webhook: waitpoint tokens for Sume runs
Create a Trigger.dev waitpoint token, send token.url as a Sume run's webhook_url, and wait.forToken() returns when Sume POSTs the run's result.
- Vercel AI SDK: generate video with a Sume API tool call
Generate video from the Vercel AI SDK with a tool() that calls Sume's POST /v1/videos on your server, returns the job id, and polls for the clip.
Written by Sume