Agent skill
Plane Webhooks Skill
Receive and verify Plane webhooks. Use when setting up Plane webhook handlers, debugging X-Plane-Signature verification, or handling project management events like workitem.created, workitem.updated, workitem.comment.created, project.updated, or cycle.created.
Install this skill
npx skills add hookdeck/webhook-skills --skill plane-webhooks
When to Use This Skill
- How do I receive Plane webhooks?
- How do I verify the
X-Plane-Signatureheader? - Why is my Plane webhook signature verification failing?
- How do I handle
workitem.created,workitem.updated,workitem.comment.created,project.updatedorcycle.createdevents from Plane? - How do I deduplicate Plane webhook deliveries (
event_idvsdelivery_id)?
Verification (core)
Plane signs the raw request body with HMAC-SHA256 keyed on the webhook secret key (plane_wh_..., used as plain UTF-8 bytes). X-Plane-Signature is the bare hex digest: no prefix, and nothing else (no timestamp or ID) is signed. Check the header is exactly 64 hex characters, then compare in constant time. Reject a missing, malformed or mismatched signature with 401.
Node:
const crypto = require('crypto');
function verify(rawBody, signature, secret) {
if (typeof signature !== 'string' || !/^[0-9a-fA-F]{64}$/.test(signature) || !secret) return false;
const expected = crypto.createHmac('sha256', Buffer.from(secret, 'utf8')).update(rawBody).digest();
const received = Buffer.from(signature.toLowerCase(), 'hex'); // safe: format validated above
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}
Python:
import hmac, hashlib, re
def verify(raw_body: bytes, signature: str | None, secret: str | None) -> bool:
if not signature or not secret or not re.fullmatch(r"[0-9a-fA-F]{64}", signature, re.ASCII):
return False
expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature.lower().encode("ascii"), expected.encode("ascii"))
rawBody must be the exact bytes received (a Buffer, not a parsed or re-serialized object). In Express use express.raw({ type: '*/*' }); in Next.js use await request.arrayBuffer(); in FastAPI use await request.body(). Parse the JSON only after the check passes.
For complete handlers with route wiring, event dispatch, and tests, see:
Common Event Types
Dispatch on the body's event field (dot notation); the same value is sent in the X-Plane-Event header.
| Event | Description |
|---|---|
workitem.created | A work item is created |
workitem.updated | A work item is updated (previous_attributes has the old values) |
workitem.deleted | A work item is deleted (data is {}) |
workitem.comment.created | A comment is added to a work item |
project.created / project.updated | A project is created or updated |
cycle.created / cycle.updated | A cycle is created or updated |
Plane documents 43 events across projects, cycles, modules, milestones, pages, page comments, work items, comments, links, votes, attachments, relations, dependencies and page links. See references/overview.md for the full list.
Full event reference: Plane webhooks
Important Headers
| Header | Description |
|---|---|
X-Plane-Signature | Hex HMAC-SHA256 of the raw body (no prefix) |
X-Plane-Event | Event type, e.g. workitem.created (matches event in the body) |
X-Plane-Delivery | UUID for this delivery attempt (matches delivery_id in the body) |
Delivery and Retries
- Any
2xxis a success. A5xxor unreachable endpoint is retried with exponential backoff (about 10 minutes base, with jitter). After 5 failed attempts Plane disables the webhook and emails its creator. 4xxresponses are not retried, so a401for a bad signature ends that delivery.event_idis stable across retries (deduplicate on it);delivery_idis new for each attempt.- No timestamp is signed, so there is no replay window to check.
Environment Variables
PLANE_WEBHOOK_SECRET=plane_wh_your_secret_here # The webhook's secret key, from the CSV download or the edit form
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 plane --path /webhooks/plane
Reference Materials
- references/overview.md - Plane webhook concepts, events and payload structure
- references/setup.md - Creating the webhook and getting the secret key
- references/verification.md - Signature verification details and gotchas