Agent skill
ClickUp Webhooks Skill
Receive and verify ClickUp webhooks. Use when setting up ClickUp webhook handlers, debugging X-Signature verification, or handling ClickUp events like taskCreated, taskUpdated, taskStatusUpdated, or taskCommentPosted.
Install this skill
npx skills add hookdeck/webhook-skills --skill clickup-webhooks
When to Use This Skill
- How do I receive ClickUp webhooks?
- How do I verify the ClickUp
X-Signaturewebhook header? - How do I handle
taskCreated,taskUpdated, ortaskStatusUpdatedevents? - Why is my ClickUp webhook signature verification failing?
- Why did my ClickUp webhook get suspended?
Verification (core)
ClickUp signs every webhook request with HMAC-SHA256 over the raw request body, keyed with the webhook.secret returned when you create the webhook through the API. The digest is sent hex-encoded in the X-Signature header (no prefix, no timestamp). Compare timing-safe.
Node:
const crypto = require('crypto');
function verifyClickUpWebhook(rawBody, signature, secret) {
if (!signature || !secret) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
try {
return crypto.timingSafeEqual(Buffer.from(signature.toLowerCase()), Buffer.from(expected));
} catch {
return false; // different lengths = invalid
}
}
Python:
import hmac, hashlib
def verify_clickup_webhook(raw_body: bytes, signature: str | None, secret: str | None) -> bool:
if not signature or not secret:
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature.lower(), expected)
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Do not return 401 for a bad signature. ClickUp immediately suspends a webhook that answers 401 (or 410). The examples return 400.
ClickUp has no official SDK helper for webhook verification, so all three examples verify manually.
Common Event Types
The event name is in the JSON body's event field (there is no event header).
| Event | Triggered When |
|---|---|
taskCreated | A task is created (a taskStatusUpdated is also sent) |
taskUpdated | A task is updated |
taskDeleted | A task is deleted |
taskStatusUpdated | A task's status changes |
taskAssigneeUpdated | An assignee is added or removed |
taskCommentPosted | A comment is added to a task |
listCreated / listUpdated / listDeleted | List lifecycle |
goalCreated / keyResultUpdated | Goal and Target (key result) changes |
Subscribe to * to receive all events. See references/overview.md for the full list of 28 events.
Important Headers and Fields
| Item | Description |
|---|---|
X-Signature header | Hex HMAC-SHA256 of the raw body |
event (body) | Event name, e.g. taskCreated |
webhook_id (body) | ID of the webhook registration that fired |
history_items[].id (body) | Use {webhook_ as the idempotency key |
Environment Variables
CLICKUP_WEBHOOK_SECRET=your_webhook_secret # webhook.secret from the Create Webhook API response
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 clickup --path /webhooks/clickup
Use the Hookdeck URL as the endpoint when you call ClickUp's Create Webhook API.
Reference Materials
- references/overview.md - ClickUp webhook concepts, all event names, payload shape
- references/setup.md - Create a webhook via the API and get the signing secret
- references/verification.md - Signature verification details and gotchas