Retool Workflow webhook needs X-Workflow-Api-Key: use a relay
Retool Workflows authenticate webhooks with an X-Workflow-Api-Key header or query parameter. Sume documents no custom delivery headers: relay after verifying.

Verify the Sume signature in a small relay, then forward to the Retool Workflow URL with the X-Workflow-Api-Key header added there. Retool authenticates webhook events with that header or a workflowApiKey query parameter, and the Sume docs document no way to add custom headers to a delivery.
Sume facts are from the Webhooks and Verifying webhooks docs; the Retool text was read 2026-09-30.
How does Retool authenticate a webhook?
Retool lists two ways: the X-Workflow-Api-Key header, or the workflowApiKey query parameter. It advises the header, because a query parameter “can be logged since it is part of the URL.”
| Option | Where the key goes | Concern |
|---|---|---|
| Retool header | X-Workflow-Api-Key | Sume documents no custom delivery headers |
| Retool query parameter | workflowApiKey in the URL | Can be logged, per Retool |
| Relay | Header added by your code | You run one more endpoint |
Can I put the key in the webhook_url?
The docs do not say whether Sume keeps a query string on webhook_url, and the URL you register will appear in your own settings and logs. A relay avoids both questions and lets you check the Sume signature before anything reaches Retool.
What does the relay do?
It reads the raw body, verifies, and forwards. Two environment values are required, and the handler refuses to run without them.
import { verifyWebhook } from "@sume-com/sdk";
export default {
async fetch(request: Request, env: Record<string, string>) {
const secret = env.SUME_COM_WEBHOOK_SIGNING_SECRET;
if (!secret || !env.RETOOL_URL || !env.RETOOL_KEY) {
return new Response("not configured", { status: 500 });
}
const body = await request.text();
if (!(await verifyWebhook({ body, headers: request.headers, secret }))) {
return new Response("bad signature", { status: 401 });
}
const res = await fetch(env.RETOOL_URL, {
method: "POST",
headers: {
"content-type": "application/json",
"X-Workflow-Api-Key": env.RETOOL_KEY,
},
body,
});
return new Response(null, { status: res.ok ? 204 : 502 });
},
};Which id should the workflow dedupe on?
Use job_id for job.* events and request_id for run events. Return 502 when the forward fails so Sume retries.
Sources
Related posts
More in Developers
- Rough cut from a script by API: one scene per Timeline slot
Descript Quick Design splits a script into moments with changing visuals. With Sume you map each scene to a Timeline video slot over a voiceover spine yourself.
- Search footage for a spoken phrase with an API: words with timing
DaVinci Resolve 21 lists IntelliSearch. To find a spoken phrase in a clip by API, transcribe with video inspect and search words[] in your own code.
- Create vs read rate limits: Managed Agents 300/1,200, Sume plans
Anthropic's Managed Agents limit creates and reads separately. Sume splits its per-minute budget the same way by plan, so polling cannot starve submits.
- Submitting 20 avatar videos at once: what each Sume plan accepts
Sume accepts as many paid jobs as concurrency plus queue allows: 6 on Free, 24 on Pro, 48 on Startup, 120 on Scale. Past that a submit returns 429 queue_full.
Written by Sume