ClickUp taskStatusUpdated webhook: start a Sume Format run

Start a Sume Format run when a ClickUp task changes status: verify X-Signature, derive the Idempotency-Key from the task, cap spend, and write the result back.

4 min readSume
All posts

To start a Sume Format run when a ClickUp task changes status, register a ClickUp webhook for taskStatusUpdated, verify its X-Signature header, and call POST /v1/formats/{handle}/{slug}/runs from your handler with an Idempotency-Key built from the task. ClickUp does not call Sume directly: the run starts from your server, which also writes the result back.

ClickUp's pages say little about retries, so the safe design assumes your handler can see the same event twice. That makes the idempotency key the most important line in the integration.

What ClickUp documents

ClickUp's Webhooks page lists task events including taskCreated, taskUpdated, taskStatusUpdated, taskMoved and taskCommentPosted (read 2026-10-10). It notes that if the user who created a webhook is disabled, the webhook remains but stops triggering. The page I read does not document retry behavior, timeouts or a health state, so this post does not assume any.

The Webhook signature page says the X-Signature header carries an HMAC with the SHA-256 algorithm computed over the request body, and that the creation response returns webhook.secret, which you store for verification (read 2026-10-10).

ClickUp webhook facts (ClickUp API documentation, read 2026-10-10) and the Sume call they map to
StepClickUp sideSume side
TriggertaskStatusUpdated eventPOST /v1/formats/{handle}/{slug}/runs from your handler
AuthenticityX-Signature, HMAC SHA-256 of the body, secret from webhook.secretBearer key with the formats:write scope
Duplicate deliveryRetry behavior not documented on the pages readIdempotency-Key: replay returns 200, idempotency_hit: true
Result backYour own call to ClickUp after the runformat.run.terminal webhook or GET /v1/format-runs/{run_id}

The handler, in order

Read the raw body, check X-Signature with the stored secret, and refuse the request when the secret is empty. Then filter on the new status: act only on the status that means "ready for a video", and ignore everything else with a 2xx. Respond quickly and start the run in the background.

Build the key from a stable task identifier your payload carries plus a version, for example clickup-<task id>-brief-v1. Bump the version only when a person asks for a fresh take. A repeated event then returns the original receipt with idempotency_hit: true, and Sume does not start or charge a second run. The key is scoped to one Format and may be up to 255 characters.

Shape the run for a task

Put the task's fields, such as title, description text and asset URLs, in input. It is caller data with at most 64 top-level keys and 2 MiB, and Sume tells the agent it is data, not instructions. Keep the decisions you want applied, like tone and length, in instruction.

Bind an output_schema with a media file for the deliverable and set primary_output_key, so the receipt gives you one primary_output_url to attach. Values you send in input, including the task id, do not come back in output, so keep a table from the run id to the task on your side. Add generation_spend_cap_usd per run; a Format without a cap defaults to $400 and the platform maximum is $500.

Write the result back

Send communication.webhook_url pointing at your own /hooks/sume. Sume posts one signed event when the run completes or fails, so you do not poll. Branch on outcome: ok means usable output, degraded means real files exist in artifacts[] but the schema was not satisfied, and error means the run failed. Then post a comment or attachment to the task with your own ClickUp API call.

Test the whole path with a throwaway task before you point it at a busy list. Use POST /v1/webhooks/test-deliveries to check your /hooks/sume route, and a low generation_spend_cap_usd for the first real run, so a wrong status filter costs little. The receipt's usage block shows the generation spend counted against the cap while the run is in progress.

A canceled or skipped run never delivers a webhook, so if your flow can cancel runs, read the cancel response instead of waiting. If your endpoint is down, the run is unchanged and result_url still returns the receipt.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume