Under Notifications a team adds Slack or Discord incoming webhooks, or a generic HTTPS webhook. Each channel picks notifyOn: all runs, findings only, or critical only. Delivery is a single attempt with a 5 s timeout, https only, redirects not followed — a failed delivery never fails the check. The payload carries what the run log already stores: rule ids, severities, messages — never schema, never credentials.
Generic webhooks are signed. Every request carries X-Bolvrk-Event (check.completed), X-Bolvrk-Delivery (a unique id) and X-Bolvrk-Signature: sha256=<hex HMAC-SHA256 of the raw body with the channel secret>. The body is a versioned envelope (version: "1").
{
"event": "check.completed",
"version": "1",
"check": {
"id": 4211,
"migrationName": "0042_region.sql",
"source": "ci",
"findingCount": 1,
"worstSeverity": "warning",
"findings": [{ "ruleId": "BV003", "severity": "warning", "message": "…" }],
"createdAt": "2026-09-02T09:14:07.000Z"
}
}import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody: the request body as received, before any JSON parsing
function verify(rawBody, headers, secret) {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
const given = headers['x-bolvrk-signature'] ?? '';
return given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}