Developers
POST events to your endpoint as they happen. Signed payloads, at-least-once delivery with retries, and one-click replay.
30+ event types across issues, comments, automations, releases, and inbox. Issues created and updated, comments added, automations run, SLAs breached — pick the events you care about and we'll POST them to your endpoint as `issue.created`, `comment.added`, `automation.run`, and more.
Every payload is HMAC-SHA-256 signed over the raw request body and a timestamp, in the standard `t=…,v1=…` format. Verify the timestamp to reject replays, then the signature with your webhook secret before acting — the SDK ships a one-line verifier so you never hand-roll the comparison.
Because delivery is at-least-once, a payload can arrive more than once. Every event carries a stable `id` and an `Idempotency-Key` header — dedupe on either and your handler stays exactly-once even when we retry.
Failed deliveries (non-2xx, timeout, or connection error) retry with exponential backoff for up to 24 hours, then park as failed. Endpoints that stay down are auto-disabled and surfaced in the dashboard so a dead consumer never silently drops events.
See every delivery, its status code, latency, and the raw response body in the dashboard. Inspect the exact payload we sent, replay it against a fixed endpoint, and rotate signing secrets with an overlap window so no in-flight delivery breaks.
import { verifyWebhook } from "@planoda/sdk/webhooks";
export async function POST(req: Request) {
const header = req.headers.get("x-planoda-signature")!;
const payload = await req.text();
const ok = await verifyWebhook({
payload,
header,
secret: process.env.PLANODA_WEBHOOK_SECRET!,
});
if (!ok) return new Response("bad signature", { status: 401 });
const event = JSON.parse(payload);
// handle event.type ...
}Full reference in the docs.
Start building
Free tier includes API access. Build, automate, and integrate.
Webhooks in short
A representative slice of the 30+ event types. Subscribe per event, scoped to a workspace.
issue.createdA new issue is openedissue.updatedState, assignee, priority, or fields changeissue.state_changedAn issue moves between workflow statesissue.deletedAn issue is archived or deletedcomment.addedSomeone comments on an issuecomment.resolvedA comment thread is resolvedreaction.addedA reaction is added to a commentautomation.runAn automation rule firessla.breachedAn SLA target is missedcycle.completedA cycle (sprint) closesproject.updatedA project's status or health changesrelease.publishedA release shipsinbox.item_createdA new inbox item lands for routing{
"id": "evt_3f9c2a",
"type": "issue.created",
"createdAt": "2026-06-17T09:24:00Z",
"workspace": "acme",
"data": {
"id": "iss_8f3a2c",
"identifier": "ENG-142",
"title": "Auth fails on Safari 17",
"priority": "high",
"url": "https://planoda.com/acme/issue/ENG-142"
}
}POST /hooks/planoda HTTP/1.1
Content-Type: application/json
X-Planoda-Signature: t=1716700000,v1=4f9a…
X-Planoda-Event: issue.created
Idempotency-Key: evt_3f9c2a
User-Agent: Planoda-Webhooks/1.0Recompute the HMAC over the raw body and compare against v1 in constant time; reject when t is too old to stop replays.
FAQ
Everything you need to build on Planoda.