Back to Skills

linear-webhooks-events

verified
View on GitHub

Marketplace

claude-code-plugins-plus

jeremylongshore/claude-code-plugins-plus-skills

Plugin

linear-pack

productivity

Repository

jeremylongshore/claude-code-plugins-plus-skills
1.1kstars

plugins/saas-packs/linear-pack/skills/linear-webhooks-events/SKILL.md

Last Verified

January 22, 2026

Install Skill

Select agents to install to:

Scope:
npx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/blob/main/plugins/saas-packs/linear-pack/skills/linear-webhooks-events/SKILL.md -a claude-code --skill linear-webhooks-events

Installation paths:

Claude
.claude/skills/linear-webhooks-events/
Powered by add-skill CLI

Instructions

# Linear Webhooks & Events

## Overview
Set up and handle Linear webhooks for real-time event notifications.

## Prerequisites
- Linear workspace admin access
- Public endpoint for webhook delivery
- Webhook signing secret configured

## Available Event Types

| Event Type | Description |
|------------|-------------|
| `Issue` | Issue created, updated, or removed |
| `IssueComment` | Comment added or updated |
| `Project` | Project changes |
| `Cycle` | Cycle (sprint) changes |
| `Label` | Label changes |
| `Reaction` | Emoji reactions |

## Instructions

### Step 1: Create Webhook Endpoint
```typescript
// api/webhooks/linear.ts (Vercel/Next.js style)
import crypto from "crypto";
import type { NextApiRequest, NextApiResponse } from "next";

export const config = {
  api: {
    bodyParser: false, // Need raw body for signature
  },
};

async function getRawBody(req: NextApiRequest): Promise<string> {
  const chunks: Buffer[] = [];
  for await (const chunk of req) {
    chunks.push(chunk);
  }
  return Buffer.concat(chunks).toString("utf8");
}

function verifySignature(payload: string, signature: string): boolean {
  const secret = process.env.LINEAR_WEBHOOK_SECRET!;
  const hmac = crypto.createHmac("sha256", secret);
  const expectedSignature = hmac.update(payload).digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method not allowed" });
  }

  const rawBody = await getRawBody(req);
  const signature = req.headers["linear-signature"] as string;

  if (!signature || !verifySignature(rawBody, signature)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(rawBody);

  // Process event
  await processLinearEvent(event);

  return res.status(200).json({ received: true });
}
```

### Step 2: Event

Validation Details

Front Matter
Required Fields
Valid Name Format
Valid Description
Has Sections
Allowed Tools
Instruction Length:
7294 chars