verifyWebhook in @sume-com/sdk: a Next.js route that returns 204
Use verifyWebhook from @sume-com/sdk in a fetch-style route: read request.text(), verify, store, return 204. It handles rotation headers.

verifyWebhook from @sume-com/sdk checks the sume-v1 HMAC signature for you. In a fetch-style route, read await request.text() before any parsing, pass it with the headers and your secret, return 401 on failure, store the event, and answer 204. It is async, takes no client and makes no request.
The route
This matches the SDK doc. The secret comes from SUME_COM_WEBHOOK_SIGNING_SECRET, the name Sume's delivery worker uses. Read it from the dashboard Webhooks tab or GET /v1/webhooks/signing-secret. The guard on the secret makes a missing variable a loud failure, not a silent pass.
import { verifyWebhook } from "@sume-com/sdk";
export async function POST(request: Request) {
const secret = process.env.SUME_COM_WEBHOOK_SIGNING_SECRET;
if (!secret) return new Response("no secret", { status: 500 });
const body = await request.text(); // raw, before JSON.parse
const ok = await verifyWebhook({
body,
headers: request.headers,
secret,
});
if (!ok) return new Response("bad signature", { status: 401 });
const event = JSON.parse(body);
await record(event.job_id ?? event.request_id, event);
return new Response(null, { status: 204 });
}Why 204 and a fast reply
Sume gives each attempt 10 seconds and tries up to 10 times, 30 seconds apart. Store the event durably, return a 2xx, and do slow work after. A slow handler gets the same event retried.
Rotation and versions
During a rotation window the signature header holds two entries, newest first. verifyWebhook in @sume-com/sdk 0.2.0, the release the docs name as current, handles that. A hand-rolled equality check does not, so prefer the SDK function or the docs verifier that loops over entries.
Idempotent storage
Key on job_id for job events and on request_id for run events. Retries, Redeliver and your own sweeps can produce the same event twice, so the second insert must be harmless.
Related posts
More in Developers
- Vertical 9:16 text-to-video API: a 12-second Wan 3.0 clip, priced
A 12-second 9:16 text-to-video request on Sume with Wan 3.0 costs $0.75 at 480p, $1.50 at 720p and $3.00 at 1080p. Full body and the other models that fit.
- OpenRouter video lists 4-8 second clips; Sume model ranges run 2-30s
OpenRouter's video guide shows typical 4-8 second durations. Sume validates duration per model, from 2-30s on Wan 3.0 to 3-10s on Omni 1.1. See the table.
- Video edit on Sume: video_url cannot ride with image fields
For gemini-omni-flash-1.1 video-to-video edit, video_url is the source, not a reference. Mixing it with image_url or reference lists fails. Fix and examples.
- Video filter 400 video_filter_ops_empty: send one op or a filtergraph
Video filter 1.0 needs at least one op in ops[] (max 8) or a non-empty filtergraph. POST /v1/video-filter/check returns diagnostics for free before you pay.
Written by Sume