Cloudflare Worker: Sume webhook to GitHub repository_dispatch
A Worker can verify the Sume signature with Web Crypto and forward only the job id and URL to GitHub's repository_dispatch endpoint, which answers 204.

A Cloudflare Worker is a small place to terminate a Sume job webhook: it verifies the signature with Web Crypto, then calls GitHub's repository_dispatch endpoint so an Actions workflow runs when the asset is ready. Forward only the job id, the event name and the result URL, because GitHub caps the payload.
Do the verification in the Worker, not in the workflow. By the time a workflow starts, the unsigned dispatch looks like any other trigger, and anyone with a token that can dispatch could fake it.
What each side requires
Sume signs {timestamp}.{raw_body} with HMAC-SHA256 and sends sume-v1=<hex> in x-sume-webhook-signature, with several comma-separated entries while a secret is rotating. The timestamp header is in seconds and the documented replay tolerance is 5 minutes.
GitHub's endpoint is POST /repos/{owner}/{repo}/dispatches. It takes an event_type of at most 100 characters and an optional client_payload with no more than 10 top-level properties and under 65,535 characters, and it answers 204 with no body on success. Cloudflare's Web Crypto page lists HMAC for signing and verifying, and adds a non-standard crypto.subtle.timingSafeEqual for comparing digests.
| Constraint | Value | Handling in the Worker |
|---|---|---|
| Sume replay window | 5 minutes, timestamp in seconds | Reject older or future timestamps |
| Sume signature header | sume-v1=hex, possibly several entries | Accept if any entry matches |
| repository_dispatch event_type | 100 characters at most | Fixed string such as sume-job |
| client_payload | 10 top-level properties, under 65,535 characters | Send job_id, event and url only |
| GitHub success | 204 No Content | Treat anything else as a failure and return 502 |
The Worker
Set SUME_COM_WEBHOOK_SIGNING_SECRET and GH_TOKEN as Worker secrets. The token needs permission to dispatch to the target repository; check the GitHub page for the scope your token type requires. An empty signing secret is refused outright.
export default {
async fetch(req, env) {
const secret = env.SUME_COM_WEBHOOK_SIGNING_SECRET;
const ts = req.headers.get("x-sume-webhook-timestamp") ?? "";
const sigs = (req.headers.get("x-sume-webhook-signature") ?? "").split(",");
const raw = await req.text();
if (!secret || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return new Response("no", { status: 401 });
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret),
{ name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
const mac = new Uint8Array(await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`${ts}.${raw}`)));
const want = "sume-v1=" + [...mac].map((b) => b.toString(16).padStart(2, "0")).join("");
const ok = sigs.some((s) => s.trim().length === want.length &&
crypto.subtle.timingSafeEqual(new TextEncoder().encode(s.trim()), new TextEncoder().encode(want)));
if (!ok) return new Response("bad signature", { status: 401 });
const evt = JSON.parse(raw);
const gh = await fetch(`https://api.github.com/repos/${env.GH_REPO}/dispatches`, {
method: "POST",
headers: { Authorization: `Bearer ${env.GH_TOKEN}`, "User-Agent": "sume-bridge",
Accept: "application/vnd.github+json" },
body: JSON.stringify({ event_type: "sume-job", client_payload: {
job_id: evt.job_id, event: evt.event, url: evt.payload?.artifacts?.[0]?.url } }),
});
return new Response(null, { status: gh.status === 204 ? 200 : 502 });
},
};Make the workflow safe to run twice
Sume redelivers when your Worker returns non-2xx, up to 10 attempts, and a manual redeliver is always available, so the same job id can arrive more than once. Start the workflow with a concurrency group named after github.event.client_payload.job_id and make its first step skip if the artifact is already published. The Worker stays stateless; the dedupe lives where the side effect does.
Sources
Related posts
More in Integrations
- Codex MCP tool_timeout_sec defaults to 60: does Sume jobs_wait fit?
Codex config.toml tool_timeout_sec defaults to 60 and startup_timeout_sec to 10. Sume jobs_wait holds up to 55 seconds, so the defaults fit by a thin margin.
- Comfy MCP and Sume MCP in one Claude Code session
Connect Comfy's cloud MCP and Sume's hosted MCP to one Claude Code session: add both servers, read the auth and billing of each, and route work between them.
- Content API for Shopping 410 Gone: send product video via Merchant API
Content API for Shopping now returns HTTP 410 Gone without an extension. Move your feed to Merchant API productInputs and add videoLinks, with a Sume clip URL.
- Copilot CLI sandbox network bypass prompt: allow Sume hosts
Copilot CLI 1.0.92-3 offers a network bypass prompt when the sandbox proxy blocks a destination. Which Sume hosts to expect and when to approve.
Written by Sume