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-Signature webhook header?
  • How do I handle taskCreated, taskUpdated, or taskStatusUpdated events?
  • 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).

EventTriggered When
taskCreatedA task is created (a taskStatusUpdated is also sent)
taskUpdatedA task is updated
taskDeletedA task is deleted
taskStatusUpdatedA task's status changes
taskAssigneeUpdatedAn assignee is added or removed
taskCommentPostedA comment is added to a task
listCreated / listUpdated / listDeletedList lifecycle
goalCreated / keyResultUpdatedGoal 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

ItemDescription
X-Signature headerHex 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_id}:{history_item_id} 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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Oct 28, 2026

View on GitHub →