agentsclimarketplace

Clickup webhooks events

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/clickup-pack/skills/clickup-webhooks-events

'Create and manage ClickUp webhooks for real-time event notifications.From its SKILL.md

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill clickup-webhooks-events

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

6.2 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it

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": "[email protected]" },
      "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.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,144. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.