Claude-code-plugins-plus-skills figma-webhooks-events
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/figma-pack/skills/figma-webhooks-events" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-figma-webhooks-events && rm -rf "$T"
manifest:
plugins/saas-packs/figma-pack/skills/figma-webhooks-events/SKILL.mdsource content
Figma Webhooks & Events
Overview
Figma Webhooks V2 push real-time notifications when files change, comments are posted, or libraries are published. Webhooks can be scoped to teams, projects, or individual files. Authentication uses a passcode echoed back in each payload.
Prerequisites
- HTTPS endpoint accessible from the internet
withFIGMA_PAT
scopewebhooks:write- Team ID (from Figma URL:
)figma.com/files/team/<TEAM_ID>/...
Instructions
Step 1: Create a Webhook
# POST /v2/webhooks -- requires webhooks:write scope curl -X POST https://api.figma.com/v2/webhooks \ -H "X-Figma-Token: ${FIGMA_PAT}" \ -H "Content-Type: application/json" \ -d '{ "event_type": "FILE_UPDATE", "team_id": "123456789", "endpoint": "https://yourapp.com/webhooks/figma", "passcode": "your-secret-passcode", "description": "Sync design tokens on file update" }' # Response: # { "id": "wh_abc123", "event_type": "FILE_UPDATE", "status": "ACTIVE", ... }
Available event types:
| Event Type | Trigger | Payload Contains |
|---|---|---|
| File saved to version history | , , |
| File deleted | , |
| Named version created | , , |
| Comment added | , , |
| Library published | , , variables |
Step 2: Handle Webhook Events
import express from 'express'; import crypto from 'crypto'; const app = express(); app.use(express.json()); // Figma webhook payload types interface FigmaWebhookBase { event_type: string; passcode: string; timestamp: string; webhook_id: string; } interface FileUpdateEvent extends FigmaWebhookBase { event_type: 'FILE_UPDATE'; file_key: string; file_name: string; triggered_by: { id: string; handle: string }; } interface FileCommentEvent extends FigmaWebhookBase { event_type: 'FILE_COMMENT'; file_key: string; file_name: string; comment: Array<{ text: string }>; comment_id: string; triggered_by: { id: string; handle: string }; } interface LibraryPublishEvent extends FigmaWebhookBase { event_type: 'LIBRARY_PUBLISH'; file_key: string; file_name: string; description: string; triggered_by: { id: string; handle: string }; } type FigmaWebhookEvent = FileUpdateEvent | FileCommentEvent | LibraryPublishEvent; app.post('/webhooks/figma', (req, res) => { const event: FigmaWebhookEvent = req.body; // 1. Verify passcode (timing-safe) const expected = process.env.FIGMA_WEBHOOK_PASSCODE!; if (event.passcode.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(event.passcode), Buffer.from(expected))) { return res.status(401).json({ error: 'Invalid passcode' }); } // 2. Respond quickly (Figma expects 200 within seconds) res.status(200).json({ received: true }); // 3. Process async processEvent(event).catch(err => console.error(`Failed to process ${event.event_type}:`, err) ); }); async function processEvent(event: FigmaWebhookEvent) { switch (event.event_type) { case 'FILE_UPDATE': console.log(`File updated: ${event.file_name} by ${event.triggered_by.handle}`); // Re-extract design tokens, invalidate cache, notify Slack await syncDesignTokens(event.file_key); break; case 'FILE_COMMENT': console.log(`Comment on ${event.file_name}: ${event.comment[0]?.text}`); // Forward to Slack, create Jira ticket, etc. break; case 'LIBRARY_PUBLISH': console.log(`Library published: ${event.file_name}`); // Trigger downstream rebuilds await triggerTokenRebuild(event.file_key); break; } }
Step 3: Manage Webhooks
const FIGMA_API = 'https://api.figma.com'; // List all webhooks for a team async function listWebhooks(teamId: string) { const res = await fetch(`${FIGMA_API}/v2/webhooks?team_id=${teamId}`, { headers: { 'X-Figma-Token': process.env.FIGMA_PAT! }, }); return res.json(); // { webhooks: [...] } } // Delete a webhook async function deleteWebhook(webhookId: string) { await fetch(`${FIGMA_API}/v2/webhooks/${webhookId}`, { method: 'DELETE', headers: { 'X-Figma-Token': process.env.FIGMA_PAT! }, }); } // Update a webhook (e.g., change endpoint) async function updateWebhook(webhookId: string, updates: Record<string, any>) { const res = await fetch(`${FIGMA_API}/v2/webhooks/${webhookId}`, { method: 'PUT', headers: { 'X-Figma-Token': process.env.FIGMA_PAT!, 'Content-Type': 'application/json', }, body: JSON.stringify(updates), }); return res.json(); }
Step 4: Idempotency for Duplicate Events
// Figma may deliver the same event multiple times const processedEvents = new Set<string>(); function deduplicateEvent(event: FigmaWebhookEvent): boolean { const key = `${event.webhook_id}:${event.timestamp}`; if (processedEvents.has(key)) { console.log(`Duplicate event skipped: ${key}`); return false; } processedEvents.add(key); // Clean up old entries (keep last 1000) if (processedEvents.size > 1000) { const oldest = Array.from(processedEvents).slice(0, 500); oldest.forEach(k => processedEvents.delete(k)); } return true; }
Output
- Webhook created and receiving Figma events
- Passcode verification on every incoming request
- Event handlers for FILE_UPDATE, FILE_COMMENT, LIBRARY_PUBLISH
- Idempotency preventing duplicate processing
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Webhook not firing | Endpoint not HTTPS | Figma requires TLS |
| Invalid passcode | Wrong secret configured | Verify passcode in webhook creation |
| Webhook status PAUSED | Too many delivery failures | Fix endpoint, then recreate webhook |
Missing | Older event format | Check webhook V2 vs V1 |
Examples
Test Webhook Locally
# Use ngrok to expose local server ngrok http 3000 # Create webhook pointing to ngrok URL curl -X POST https://api.figma.com/v2/webhooks \ -H "X-Figma-Token: ${FIGMA_PAT}" \ -H "Content-Type: application/json" \ -d '{ "event_type": "FILE_UPDATE", "team_id": "YOUR_TEAM_ID", "endpoint": "https://YOUR-NGROK.ngrok.io/webhooks/figma", "passcode": "test-passcode" }'
Resources
Next Steps
For performance optimization, see
figma-performance-tuning.