> clickup-webhooks-events

Create and manage ClickUp webhooks for real-time event notifications. Use when setting up webhook listeners for task/list/space events, implementing two-way sync, or handling ClickUp event payloads. Trigger: "clickup webhook", "clickup events", "clickup notifications", "clickup real-time", "clickup event listener", "clickup webhook create".

fetch
$curl "https://skillshub.wtf/jeremylongshore/claude-code-plugins-plus-skills/clickup-webhooks-events?format=md"
SKILL.mdclickup-webhooks-events

ClickUp Webhooks & Events

Overview

ClickUp webhooks send HTTP POST notifications when resources change. Register webhooks via API, subscribe to specific events, and receive payloads with history_items showing what changed.

Webhook Endpoints

POST   /api/v2/team/{team_id}/webhook    Create webhook
GET    /api/v2/team/{team_id}/webhook    Get webhooks
PUT    /api/v2/webhook/{webhook_id}      Update webhook
DELETE /api/v2/webhook/{webhook_id}      Delete webhook

Create a Webhook

async function createWebhook(teamId: string, endpoint: string, events: string[]) {
  return clickupRequest(`/team/${teamId}/webhook`, {
    method: 'POST',
    body: JSON.stringify({
      endpoint,        // Your HTTPS URL
      events,          // Array of event names
      space_id: null,  // Optional: limit to specific space
      folder_id: null, // Optional: limit to specific folder
      list_id: null,   // Optional: limit to specific list
      task_id: null,   // Optional: limit to specific task
    }),
  });
}

// Subscribe to task and list events
const webhook = await createWebhook('1234567', 'https://myapp.com/webhooks/clickup', [
  'taskCreated',
  'taskUpdated',
  'taskDeleted',
  'taskStatusUpdated',
  'taskAssigneeUpdated',
  'taskDueDateUpdated',
  'taskCommentPosted',
  'taskTimeTrackedUpdated',
  'listCreated',
  'listUpdated',
  'listDeleted',
]);

// Response:
// { "id": "wh_abc123", "webhook": { "id": "...", "endpoint": "...", "events": [...] } }

Available Events

CategoryEvents
TasktaskCreated, taskUpdated, taskDeleted, taskStatusUpdated, taskAssigneeUpdated, taskDueDateUpdated, taskTagUpdated, taskMoved, taskCommentPosted, taskCommentUpdated, taskTimeTrackedUpdated, taskTimeEstimateUpdated, taskPriorityUpdated
ListlistCreated, listUpdated, listDeleted
FolderfolderCreated, folderUpdated, folderDeleted
SpacespaceCreated, spaceUpdated, spaceDeleted
GoalgoalCreated, goalUpdated, goalDeleted, keyResultCreated, keyResultUpdated, keyResultDeleted

Webhook Payload Format

{
  "event": "taskUpdated",
  "webhook_id": "wh_abc123",
  "task_id": "abc123",
  "history_items": [
    {
      "id": "hist_001",
      "type": 1,
      "date": "1695000000000",
      "field": "status",
      "parent_id": "abc123",
      "data": {},
      "source": null,
      "user": { "id": 183, "username": "john", "email": "john@example.com" },
      "before": { "status": "to do", "color": "#d3d3d3", "type": "open" },
      "after": { "status": "in progress", "color": "#4194f6", "type": "custom" }
    }
  ]
}

Webhook Handler (Express)

import express from 'express';

const app = express();
app.use(express.json());

app.post('/webhooks/clickup', async (req, res) => {
  const { event, webhook_id, task_id, history_items } = req.body;

  // Immediately acknowledge (ClickUp expects 200 within 30s)
  res.status(200).json({ received: true });

  // Process asynchronously
  try {
    await processClickUpEvent(event, task_id, history_items);
  } catch (err) {
    console.error(`Failed to process ${event} for task ${task_id}:`, err);
  }
});

async function processClickUpEvent(
  event: string,
  taskId: string,
  historyItems: any[]
) {
  switch (event) {
    case 'taskCreated':
      console.log(`New task: ${taskId}`);
      break;
    case 'taskStatusUpdated': {
      const change = historyItems[0];
      console.log(`Task ${taskId}: ${change.before.status} -> ${change.after.status}`);
      // Trigger downstream actions (e.g., notify Slack, update external system)
      break;
    }
    case 'taskCommentPosted':
      console.log(`New comment on task ${taskId}`);
      break;
    case 'taskTimeTrackedUpdated':
      console.log(`Time tracked updated on task ${taskId}`);
      break;
    default:
      console.log(`Unhandled event: ${event}`);
  }
}

Idempotency (Prevent Duplicate Processing)

const processedEvents = new Map<string, number>();

function isDuplicate(webhookId: string, historyItemId: string): boolean {
  const key = `${webhookId}:${historyItemId}`;
  if (processedEvents.has(key)) return true;
  processedEvents.set(key, Date.now());

  // Clean old entries every 1000 events
  if (processedEvents.size > 10000) {
    const cutoff = Date.now() - 3600000; // 1 hour
    for (const [k, v] of processedEvents) {
      if (v < cutoff) processedEvents.delete(k);
    }
  }
  return false;
}

List and Manage Webhooks

# List all webhooks for a workspace
TEAM_ID="1234567"
curl -s "https://api.clickup.com/api/v2/team/${TEAM_ID}/webhook" \
  -H "Authorization: $CLICKUP_API_TOKEN" | jq '.webhooks[] | {id, endpoint, events}'

# Delete a webhook
curl -s -X DELETE "https://api.clickup.com/api/v2/webhook/WH_ID" \
  -H "Authorization: $CLICKUP_API_TOKEN"

Error Handling

IssueCauseSolution
Webhook not firingEndpoint not HTTPSWebhooks require HTTPS URLs
Duplicate eventsNo idempotencyTrack history_item IDs
Timeout (no 200)Slow processingRespond 200 immediately, process async
Webhook auto-disabledRepeated failuresClickUp disables after many 5xx responses

Resources

Next Steps

For performance optimization, see clickup-performance-tuning.

┌ stats

installs/wk0
░░░░░░░░░░
github stars1.7K
██████████
first seenMar 23, 2026
└────────────

┌ repo

jeremylongshore/claude-code-plugins-plus-skills
by jeremylongshore
└────────────