Webhooks
Receive signed events when posts, comments, votes, post statuses, or Linear issue links change.
Connect Boarbs to your own backend with an outbound webhook. Starter and Pro organizations can configure one HTTPS endpoint from Settings → API.
An organization admin creates the endpoint and receives its signing secret once. Store the secret securely before closing the dialog. You can update the endpoint URL, rotate the secret, or remove the webhook later.
Events
Boarbs sends these event types:
| Event | When it is sent |
|---|---|
post.created | A post is created |
comment.created | A public or internal comment is created |
post.status_changed | A post moves to another status |
vote.created | A vote is added to a post |
vote.deleted | A vote is undone |
linear.linked | A Linear issue is created from a post, or an existing issue is linked |
linear.unlinked | A Linear issue is unlinked from a post, including a Linear-side remove |
linear.status_changed | The Linear status changes on a linked issue |
Every request is an HTTPS POST with a JSON envelope:
{
"id": "event-id",
"type": "post.created",
"createdAt": "2026-08-29T10:24:00.000Z",
"data": {
"post": {}
}
}The data object depends on the event:
post.createdincludespostcomment.createdincludescommentand itspostpost.status_changedincludespost,previousStatus, andstatusvote.createdincludesvoteand itspostvote.deletedincludesvoteand itspostlinear.linkedincludespostandissuelinear.unlinkedincludespostandissuelinear.status_changedincludespostandissue
Vote objects include id, postId, userEmail, userId (nullable), and createdAt.
Linear issue objects include id, identifier, url, and status (id, name). linear.status_changed is the Linear issue status on the link. post.status_changed may still fire when that status maps to a Boarbs status.
Verify Signatures
Each delivery includes these headers:
Webhook-Id— the event IDWebhook-Timestamp— the Unix timestamp in seconds when the request was signedWebhook-Signature— the HMAC-SHA256 signature asv1=<hex>
Verify the signature against the raw request body before parsing JSON. The signed content is:
<Webhook-Id>.<Webhook-Timestamp>.<raw request body>This Node.js example compares signatures without leaking timing information:
import { createHmac, timingSafeEqual } from "node:crypto";
const webhookId = request.headers.get("webhook-id") ?? "";
const webhookTimestamp =
request.headers.get("webhook-timestamp") ?? "";
const signedContent = `${webhookId}.${webhookTimestamp}.${rawBody}`;
const expected = createHmac(
"sha256",
process.env.BOARBS_WEBHOOK_SECRET,
)
.update(signedContent)
.digest();
const signature = request.headers.get("webhook-signature") ?? "";
const provided = /^v1=[0-9a-f]{64}$/.test(signature)
? Buffer.from(signature.slice(3), "hex")
: null;
const valid =
provided?.length === expected.length &&
timingSafeEqual(provided, expected);Reject invalid signatures and timestamps outside your replay window. Keep the signing secret on the server and never expose it in browser code.
Acknowledge Deliveries
Return any 2xx response within 10 seconds to acknowledge a delivery. If the endpoint times out or returns a non-2xx response, Boarbs retries three times with increasing delays.
Handlers should be idempotent because retries can deliver the same event more than once. Use Webhook-Id as the event identifier when recording processed deliveries.
Rotate or Remove the Webhook
Use Settings → API to rotate the signing secret if it is exposed. Save the replacement secret when it is shown and update your receiver before accepting new deliveries.
Removing the webhook deletes its pending deliveries and stops future events.
Next Steps
- API — Review API authentication and available resources
- Slack Integration — Send public activity to a Slack channel
- Discord Integration — Send public activity to a Discord channel
- Linear Integration — Link posts to Linear issues