Windmill webhook token in the URL: calling it from a Sume job
Windmill prefers a bearer header, but Sume's webhook_url is just a URL, so the token must ride in the query string. How to scope it and still trust the result.

Windmill accepts its webhook token either as an Authorization: Bearer header or as a ?token= query parameter, and recommends the header. Sume's webhook_url is only a URL, so a direct Sume callback into Windmill has to use the query parameter; keep that token narrow and re-read the job from the Sume API instead of trusting the callback body.
The Windmill facts here come from its webhooks page, read on 2026-10-02.
What does Windmill give you for a webhook?
Windmill documents two URL shapes per script path. The asynchronous one, /api/w/$WORKSPACE_ID/jobs/run/$SCRIPT_PATH, returns a job UUID immediately. The synchronous one, /api/w/$WORKSPACE_ID/jobs/run_wait_result/$SCRIPT_PATH, blocks until the job finishes and returns the result. The page says plainly that asynchronous mode is always better because the client does not wait. For progress there is a server-sent events route at /api/w/<workspace>/jobs_u/getupdate_sse/<job_id>.
| Option | Behavior | Good for a Sume callback? |
|---|---|---|
| jobs/run | Returns a job UUID at once | Yes, answers fast |
| jobs/run_wait_result | Blocks until the script finishes | No, Sume allows 10 seconds per attempt |
| Bearer header token | Recommended by Windmill | Not available from webhook_url |
| token query parameter | Accepted, less safe | The only direct option |
Why must the callback use the async URL?
Sume gives each delivery attempt 10 seconds and retries a slow endpoint, as the webhook docs say. A run_wait_result URL holds the request until your script ends, so any script that does real work would burn attempts. Point Sume at jobs/run, let the script return its UUID, and do the work in the script run.
How do I limit the damage of a token in a URL?
URLs end up in logs, so treat this token as exposed. Create a token for this one purpose and give it access to only the receiving script or flow, and rotate it on a schedule. Windmill's page does not describe scoping in the part I read, so check its token settings for what your version offers.
More important, make the script distrust its input. Take job_id from the body, validate its shape, and call GET https://api.sume.com/v1/jobs/{job_id}/status with your own API key. Only act on what that call returns. A forged request then costs one status read.
What does the submit look like?
Replace the host, script path and token with your own. The query string is part of the webhook_url, which must be a public HTTPS URL.
curl -sS -X POST https://api.sume.com/v1/image-1.0/generate \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: windmill-demo-001" \
-d '{
"prompt": "a blue ceramic mug on a table",
"mode": "webhook",
"webhook_url": "https://app.windmill.dev/api/w/my-ws/jobs/run/f/sume/on_done?token=SCOPED_TOKEN"
}'What does the receiving script do?
Keep it small. It receives the JSON body, pulls out job_id, checks the shape, and calls the status endpoint with an API key stored as a Windmill secret. If the job is terminal and completed, it fetches GET /v1/jobs/{id}/result and writes the artifact URLs wherever you need them. If it is not terminal, it does nothing, since Sume sends terminal events only and a non-terminal read means the callback was not what it claimed.
Return quickly. The script's own work can continue in the flow after the webhook returns its UUID, which is the point of the async URL.
Sume retries up to 10 times at a 30-second default spacing, so a script that fails once is tried again; make the write idempotent on job_id.
Should I use sync run_wait_result anywhere?
Yes, on the other side of the integration: when a Windmill script calls Sume. A Windmill flow that submits a job should still use Sume's async mode and poll, because Sume's own sync mode waits at most 30 seconds. Windmill's run_wait_result is for a caller waiting on a Windmill script, which is a separate question from how Windmill waits on Sume.
Keep the two waits apart in your head. Windmill documents a configurable maximum timeout for sync endpoints at the instance level, and the page I read gave no default, so treat any sync call as bounded and never depend on it for a video.
If a header is a hard requirement, put a small relay in front that checks the Sume signature and forwards to Windmill with Authorization: Bearer. Retool workflow webhook API key header walks through the same relay idea for another tool.
Sources
Related posts
More in Integrations
- Storage by Zapier 32-character keys: remember a Sume job id
Storage by Zapier keys are limited to 32 characters and 500 keys, and idle keys vanish after 2 months. Key by your row id, store the Sume job id as the value.
- Zed MCP: which agents use your Sume server (Panel, ACP, terminal)
Zed's own Agent uses context_servers directly, external agents get them over ACP, and terminal CLIs read their own config. Where the Sume MCP entry goes.
- How to add an MCP server to ChatGPT with developer mode
Turn on ChatGPT developer mode, create an app for the server's URL, and sign in with OAuth. The steps, with Sume's hosted MCP server as the example.
- How to add subtitles to a video in Python
Add subtitles to a video in Python with Requests: POST the video URL to Sume's /v1/video-captions, poll the job, then read the captioned video_url.
Written by Sume