linear-webhooks-events

10
0
Source

Configure and handle Linear webhooks for real-time event processing. Use when setting up webhooks, handling Linear events, or building real-time integrations. Trigger with phrases like "linear webhooks", "linear events", "linear real-time", "handle linear webhook", "linear webhook setup".

Install

mkdir -p .claude/skills/linear-webhooks-events && curl -L -o skill.zip "https://mcp.directory/api/skills/download/2391" && unzip -o skill.zip -d .claude/skills/linear-webhooks-events && rm skill.zip

Installs to .claude/skills/linear-webhooks-events

About this skill

Linear Webhooks & Events

Overview

Set up and handle Linear webhooks for real-time event processing. Linear sends webhook payloads for issues, comments, projects, cycles, and other entities with HMAC-SHA256 signature verification.

Prerequisites

  • Linear workspace admin access
  • Public HTTPS endpoint for webhook delivery
  • Webhook signing secret (generated during webhook creation)

Instructions

Step 1: Create Webhook Endpoint

Build a webhook receiver with signature verification.

import express from "express";
import crypto from "crypto";

const app = express();

// IMPORTANT: parse raw body for signature verification
app.post("/webhooks/linear", express.raw({ type: "*/*" }), (req, res) => {
  const signature = req.headers["linear-signature"] as string;
  const delivery = req.headers["linear-delivery"] as string;
  const eventType = req.headers["linear-event"] as string;
  const body = req.body.toString();

  // Verify HMAC-SHA256 signature
  const expected = crypto
    .createHmac("sha256", process.env.LINEAR_WEBHOOK_SECRET!)
    .update(body)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    console.error(`Invalid signature for delivery ${delivery}`);
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(body);
  console.log(`Received ${eventType} ${event.action} (delivery: ${delivery})`);

  // Process asynchronously, respond quickly
  processEvent(event).catch(err => console.error("Processing failed:", err));
  res.status(200).json({ received: true });
});

app.listen(3000);

Step 2: Event Processing Router

Route events by entity type and action.

interface LinearWebhookPayload {
  action: "create" | "update" | "remove";
  type: string;  // "Issue", "Comment", "Project", "Cycle", etc.
  data: Record<string, any>;
  url: string;
  actor?: { id: string; type: string; name?: string };
  updatedFrom?: Record<string, any>;  // Previous values on update
  createdAt: string;
  webhookTimestamp: number;
}

type EventHandler = (event: LinearWebhookPayload) => Promise<void>;

const handlers: Record<string, Record<string, EventHandler>> = {
  Issue: {
    create: async (e) => {
      console.log(`New issue: ${e.data.identifier} — ${e.data.title}`);
      await notifySlack(`New issue: [${e.data.identifier}](${e.url}) ${e.data.title}`);
    },
    update: async (e) => {
      // Check what changed using updatedFrom
      if (e.updatedFrom?.stateId) {
        console.log(`${e.data.identifier} state changed to ${e.data.state?.name}`);
        if (e.data.state?.type === "completed") {
          await notifySlack(`Done: [${e.data.identifier}](${e.url}) ${e.data.title}`);
        }
      }
      if (e.updatedFrom?.assigneeId) {
        console.log(`${e.data.identifier} assigned to ${e.data.assignee?.name}`);
      }
    },
    remove: async (e) => {
      console.log(`Issue deleted: ${e.data.identifier}`);
    },
  },
  Comment: {
    create: async (e) => {
      console.log(`New comment on ${e.data.issue?.identifier}: ${e.data.body?.substring(0, 80)}`);
    },
  },
  Project: {
    update: async (e) => {
      if (e.updatedFrom?.state) {
        console.log(`Project "${e.data.name}" status: ${e.data.state}`);
      }
    },
  },
};

async function processEvent(event: LinearWebhookPayload): Promise<void> {
  const handler = handlers[event.type]?.[event.action];
  if (handler) {
    await handler(event);
  } else {
    console.log(`Unhandled: ${event.type}.${event.action}`);
  }
}

Step 3: Idempotent Event Processing

Linear may retry webhook deliveries. Use the Linear-Delivery header as a deduplication key.

const processedDeliveries = new Set<string>();

function isNewDelivery(deliveryId: string): boolean {
  if (processedDeliveries.has(deliveryId)) return false;
  processedDeliveries.add(deliveryId);
  // Clean up old entries periodically
  if (processedDeliveries.size > 10000) {
    const entries = [...processedDeliveries];
    entries.slice(0, 5000).forEach(id => processedDeliveries.delete(id));
  }
  return true;
}

// In webhook handler:
app.post("/webhooks/linear", express.raw({ type: "*/*" }), (req, res) => {
  const delivery = req.headers["linear-delivery"] as string;
  // ... signature verification ...

  if (!isNewDelivery(delivery)) {
    return res.status(200).json({ status: "duplicate, skipped" });
  }

  // Process event...
});

Step 4: Register Webhook in Linear

# Via Linear UI: Settings > API > Webhooks > New webhook
# URL: https://your-app.com/webhooks/linear
# Select resource types: Issues, Comments, Projects

# Or via API:
curl -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation { webhookCreate(input: { url: \"https://your-app.com/webhooks/linear\", resourceTypes: [\"Issue\", \"Comment\", \"Project\"], allPublicTeams: true }) { success webhook { id enabled } } }"
  }'

Step 5: Local Development with ngrok

# Start your webhook server
npm run dev  # listening on port 3000

# In another terminal, expose via ngrok
ngrok http 3000
# Copy the https://*.ngrok.io URL

# Create a webhook in Linear with the ngrok URL
# Settings > API > Webhooks > New webhook
# URL: https://abc123.ngrok.io/webhooks/linear

Error Handling

ErrorCauseSolution
Invalid signature (401)Wrong webhook secret or body parsingUse express.raw(), verify secret matches Linear settings
Webhook not receivedURL not publicly accessibleCheck HTTPS, firewall, ngrok tunnel
Duplicate processingLinear retried deliveryDeduplicate using Linear-Delivery header
Timeout on deliveryHandler takes too longRespond 200 immediately, process async
Missing updatedFromField didn't changeupdatedFrom only contains changed fields

Examples

Slack Notification on Issue State Change

async function notifySlack(message: string) {
  await fetch(process.env.SLACK_WEBHOOK_URL!, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ text: message }),
  });
}

// In Issue.update handler:
if (e.updatedFrom?.stateId && e.data.state?.type === "completed") {
  await notifySlack(
    `:white_check_mark: *${e.data.identifier}* completed by ${e.actor?.name}\n${e.data.title}`
  );
}

List Active Webhooks

const client = getLinearClient();
const webhooks = await client.webhooks();
for (const wh of webhooks.nodes) {
  console.log(`${wh.url} — enabled: ${wh.enabled}, types: ${wh.resourceTypes}`);
}

Output

  • Express webhook endpoint with HMAC-SHA256 signature verification
  • Event router dispatching by entity type and action
  • Idempotent delivery processing using deduplication set
  • Webhook registration via API and UI
  • ngrok setup for local development testing

Resources

Next Steps

Optimize performance with linear-performance-tuning.

svg-icon-generator

jeremylongshore

Svg Icon Generator - Auto-activating skill for Visual Content. Triggers on: svg icon generator, svg icon generator Part of the Visual Content skill category.

6814

d2-diagram-creator

jeremylongshore

D2 Diagram Creator - Auto-activating skill for Visual Content. Triggers on: d2 diagram creator, d2 diagram creator Part of the Visual Content skill category.

2212

performing-penetration-testing

jeremylongshore

This skill enables automated penetration testing of web applications. It uses the penetration-tester plugin to identify vulnerabilities, including OWASP Top 10 threats, and suggests exploitation techniques. Use this skill when the user requests a "penetration test", "pentest", "vulnerability assessment", or asks to "exploit" a web application. It provides comprehensive reporting on identified security flaws.

379

designing-database-schemas

jeremylongshore

Design and visualize efficient database schemas, normalize data, map relationships, and generate ERD diagrams and SQL statements.

978

performing-security-audits

jeremylongshore

This skill allows Claude to conduct comprehensive security audits of code, infrastructure, and configurations. It leverages various tools within the security-pro-pack plugin, including vulnerability scanning, compliance checking, cryptography review, and infrastructure security analysis. Use this skill when a user requests a "security audit," "vulnerability assessment," "compliance review," or any task involving identifying and mitigating security risks. It helps to ensure code and systems adhere to security best practices and compliance standards.

86

django-view-generator

jeremylongshore

Generate django view generator operations. Auto-activating skill for Backend Development. Triggers on: django view generator, django view generator Part of the Backend Development skill category. Use when working with django view generator functionality. Trigger with phrases like "django view generator", "django generator", "django".

15

You might also like

flutter-development

aj-geddes

Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.

641968

drawio-diagrams-enhanced

jgtolentino

Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.

590705

godot

bfollington

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.

339397

ui-ux-pro-max

nextlevelbuilder

"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."

318395

nano-banana-pro

garg-aayush

Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.

450339

fastapi-templates

wshobson

Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.

304231

Stay ahead of the MCP ecosystem

Get weekly updates on new skills and servers.