plugins/linear-orchestrator/skills/linear-webhooks/SKILL.md
This skill should be used when registering, verifying, or processing Linear webhooks — HMAC signatures, replay protection, idempotency, dead-letter queues. Activates on "linear webhook", "webhook signature", "Linear-Signature", "webhook secret".
npx skillsauth add markus41/claude Linear Webhooks (Verify, Replay, DLQ)Install this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Reference: https://linear.app/developers/webhooks
Linear signs every delivery with HMAC-SHA256:
Linear-Signature: <hex digest>
Verify in constant time:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyLinearSignature(rawBody: Buffer, signature: string, secret: string): boolean {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(signature, "hex");
const b = Buffer.from(expected, "hex");
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}
Always read the raw body bytes, not the parsed JSON. Express:
app.use("/linear/webhook", express.raw({ type: "application/json" }));
Each delivery has a webhookTimestamp field in the JSON body (Unix ms). Reject events older than 5 minutes:
if (Math.abs(Date.now() - body.webhookTimestamp) > 5 * 60_000) reject();
Linear may re-deliver. Each event has:
delivery.id — unique per delivery (use this!)data.id — entity IDStore seen delivery.id in Redis with 7-day TTL; ignore duplicates.
Issue, IssueLabel, Comment, Cycle, Project, ProjectUpdate, Initiative, InitiativeUpdate, Customer, CustomerNeed, Reaction, Attachment, Document.
Subscribe selectively — fewer types means smaller event volume.
create | update | remove. Some resources support more; consult the schema.
{
"action": "update",
"actor": { "id": "...", "name": "..." },
"createdAt": "2026-04-30T12:00:00.000Z",
"data": { /* the resource */ },
"type": "Issue",
"url": "https://linear.app/...",
"webhookTimestamp": 1714478400000,
"webhookId": "...",
"delivery": { "id": "..." }
}
Don't trust webhook payload state for reads. Linear may send out-of-order events. After receiving an Issue update, re-fetch via GraphQL using the id to get the canonical state.
Implementation in lib/webhook-dlq.ts:
/linear:webhook dlq lists; /linear:webhook replay --since 24h retries from DLQUse ngrok http 3000 and set the public URL as the webhook URL. Linear has no built-in test-replay UI; use webhookTest mutation if available, or the DLQ replay path.
delivery.id idempotencydevelopment
Enhanced plan-authoring skill with Pre-Writing context gathering, task metadata, non-TDD templates, Red Flags, telemetry, and an automated plan linter. Use when you have a spec or requirements for a multi-step task, before touching code.
tools
Documentation intelligence engine with graph-based API docs, algorithm library, and drift detection
tools
Ultraplan cloud planning — kick off a plan in the cloud from your terminal, review and revise in the browser, then execute remotely or send back to CLI
tools
--- name: mcp description: Configure MCP servers for Claude Code — stdio vs HTTP, authentication, Tools/Resources/Prompts distinction, channels (CI webhook, mobile relay, Discord bridge, fakechat), and cost of always-loaded tools. Use this skill whenever adding an MCP server, debugging connection issues, choosing between MCP Tools vs Prompts vs Resources, installing channel servers, or managing .mcp.json. Triggers on: "MCP server", "mcp config", "add Obsidian MCP", "install context7", "channels"