FDBKDocs

Developer docs

Webhooks, Slack and DiscordPro and up

Hear about new posts, status changes and comments.

Under Developers, add an endpoint and pick the events. We send a POST with JSON to your server, or a short message to a Slack or Discord channel.

  • post.created A post is created
  • post.status_changed A post changes status (including when it's merged)
  • comment.created Someone comments, or the team replies

Slack and Discord

Pick Slack or Discord and paste the channel's incoming webhook URL. In Slack, create an incoming webhook for the channel; in Discord, channel settings → Integrations → Webhooks → New webhook → Copy URL. Messages link to the post in your dashboard.

Your server

Example
POST /your/endpoint
Content-Type: application/json
FDBK-Event: post.status_changed
FDBK-Delivery: 6592c4b9-7980-4879-94e7-535c15ba782a
FDBK-Signature: t=1791046922,v1=7cb9a0f9…

{
  "id": "6592c4b9-7980-4879-94e7-535c15ba782a",
  "type": "post.status_changed",
  "created_at": "2026-10-03T17:02:00.781Z",
  "data": {
    "post": { "id": 22, "status": "in_progress", … },
    "previous_status": "planned"
  }
}

comment.created also carries data.comment. Answer any 2xx within 10 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 8 hours, then dropped. Redirects aren't followed. An endpoint that fails 20 times in a row is turned off until you turn it back on. Deliveries can arrive out of order and, rarely, twice: use the delivery id to skip repeats. Pro includes 5 endpoints and 20,000 deliveries a month, retries included.

Verifying signatures

Each endpoint has a signing secret (whsec_…). The signature is an HMAC-SHA256 of the timestamp, a dot and the raw body. Compare in constant time and reject old timestamps.

Example
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

We use Google Analytics to see how people find and use FDBK. No ads, and never on your boards.