ModelsPricingAPI keys
  • Overview
  • Quickstart
  • Authentication
  • Jobs and polling
  • Webhooks

Jobs

Webhooks

Instead of polling, pass a URL and Sume POSTs the outcome there once the job ends. Every delivery is signed.

Set a webhook

Add mode: "webhook" and webhook_url to any generate request. The URL must be public HTTPS; localhost, private-network and plain HTTP URLs are refused when you submit.

cURL
curl -X POST https://api.dev.sume.com/v1/video-router/generate \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance-2.5",
    "prompt": "A paper boat drifting down a rain gutter",
    "duration": 5,
    "mode": "webhook",
    "webhook_url": "https://example.com/hooks/sume"
  }'

Events

eventstatusWhen
job.completedOKpayload.artifacts holds the output.
job.failedERRORerror says why.
job.canceledERRORThe job was canceled.
job.completed
{
  "event": "job.completed",
  "request_id": "job_7513b52d-3669-4ffe-b317-8b02000715eb",
  "job_id": "job_7513b52d-3669-4ffe-b317-8b02000715eb",
  "status": "OK",
  "payload": {
    "artifacts": [
      {
        "id": "artf_UCdnhWThU_Tbemv7E2gE7-ee9lNCIDxP",
        "url": "https://media.sume.com/artifacts/artf_UCdnhWThU_Tbemv7E2gE7-ee9lNCIDxP/images.png",
        "type": "image",
        "content_type": "image/png"
      }
    ]
  }
}

Verify the signature

Each delivery carries two headers:

Headers
x-sume-webhook-timestamp: 1792540000
x-sume-webhook-signature: sume-v1=5f2b…

The signature is HMAC-SHA256, hex, over <timestamp>.<raw body> with your workspace's signing secret. Read the secret in Dashboard → Webhooks or from GET /v1/webhooks/signing-secret. While a secret rotates, the header holds one sume-v1= entry per live secret, comma-separated: accept the delivery if any entry matches. Refuse timestamps more than five minutes old.

import { createHmac, timingSafeEqual } from "node:crypto";

export function isSumeDelivery(rawBody, timestamp, signatureHeader, secret) {
  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!Number.isFinite(age) || age > 300) return false;
  const digest = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  const expected = Buffer.from(`sume-v1=${digest}`);
  return String(signatureHeader)
    .split(",")
    .map((entry) => Buffer.from(entry.trim()))
    .some((entry) => entry.length === expected.length && timingSafeEqual(entry, expected));
}

Verify the raw body exactly as received, before any JSON parsing.

Deliveries and retries

  • Answer with any 2xx within 10 seconds, after you have stored the event.
  • A delivery without a 2xx is tried up to 10 times: 30 seconds after the first try, then twice as long each time, never more than an hour apart.
  • A delivery can arrive more than once; dedupe on job_id.
  • The job's webhook_delivery field records what was sent. POST /v1/jobs/{id}/webhook/redeliver sends it again.
  • Keep a slow poll as a fallback in case your endpoint was down.

Related

  • Jobs and polling
  • Video
  • JavaScript client

On this page

  • Set a webhook
  • Events
  • Verify the signature
  • Deliveries and retries