jeremylongshore/claude-code-plugins-plus-skills
linear-pack
plugins/saas-packs/linear-pack/skills/linear-webhooks-events/SKILL.md
January 22, 2026
Select agents to install to:
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-eventsInstallation paths:
.claude/skills/linear-webhooks-events/# 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