Approval gate before posting AI video: verify the Sume webhook

Hold every AI video for human review before it posts. A Node verifier for Sume's signed job webhooks that refuses an empty secret, plus the review steps.

5 min readSume
All posts

Put a human step between a finished Sume job and the post: receive the signed job.completed webhook, verify the signature, and queue the video for review instead of publishing. With platforms tightening labels and spam checks, an approval gate is the cheapest control you have.

Sume signs every delivery with an HMAC over <timestamp>.<raw_body> and sends it in x-sume-webhook-signature: sume-v1=<hex>, with x-sume-webhook-timestamp beside it, as described on the webhooks page.

What the webhook gives you

Only terminal events are sent.

Sume job webhook facts (read 2026-10-07)
ItemValue
Eventsjob.completed, job.failed, job.canceled
URLPublic HTTPS only; localhost, private networks and non-HTTPS are rejected
Signaturesume-v1=<hex>, HMAC-SHA256 of timestamp + "." + raw body
RotationThe header carries one entry per live secret, newest first, comma separated
ReplayReject a timestamp outside your tolerance window

A verifier that refuses an empty secret

import crypto from "node:crypto";

export function verify({ rawBody, timestamp, signatureHeader, secret, toleranceSeconds = 300 }) {
  if (!secret) throw new Error("empty webhook secret");
  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!Number.isFinite(age) || age > toleranceSeconds) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return signatureHeader.split(",").some((part) => {
    const [scheme, sig] = part.trim().split("=");
    if (scheme !== "sume-v1" || !sig || sig.length !== expected.length) return false;
    return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
  });
}

Wire it up

Verify against the raw request body, not a parsed and re-serialized one, since any change in whitespace changes the HMAC. Read the signing secret from the dashboard Webhooks tab, or from GET /v1/webhooks/signing-secret with a key that has account:read, and keep it in an environment variable. Compare the secret fingerprint header when a signature fails, as the docs suggest.

The review step

After a valid job.completed, do not post. Store the job id and the media.sume.com URL, and send a reviewer a link. A reviewer should check:

  • The video matches the brief and no real person or brand is shown by mistake.
  • Every claim on screen has a source on your sheet.
  • The AI disclosure is ready: the platform label at upload and any on-screen line.
  • Audio and captions are right. A video inspect transcript ($0.01 per audio minute) makes the spoken claims easy to read.

Keep a poll fallback

A webhook can be missed. Jobs and results describes the poll fallback to keep alongside it, so a job that completes while your endpoint is down is still picked up. For a proof-then-final loop with clients, see the 480p proof workflow.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume