// docs / webhooks
Webhooks
FixVibe は、スキャン完了、終端エラー、高 severity finding、live monitor alert、scheduled run に対して署名付き送信 Webhook を送ります。配信は at-least-once で、約 24 時間リトライします。
設定
Account → Webhooks を開き、HTTPS endpoint を作成し、一度だけ表示される whsec_ secret を受信側に保存します。Webhook は有料プランで利用できます。
- Account → Webhooks から endpoint を作成します。
- その endpoint に FixVibe が送るイベントを選びます。
- secret はすぐにコピーしてください。一度しか表示されず、後からは rotate しかできません。
イベント
ローンチ時のイベント面は、チームが CI、alerting、ticketing に接続しやすいタイミングをカバーします:
- scan.completed — スキャンが completed に遷移したときに発火します。Payload: scan id、target、mode、severity bucket count、report link。
- scan.failed — terminal-failure notification. Payload: scan id, failure reason, and report link when available.
- finding.created — critical または high finding ごとに送信されます。低い severity は event spam を避けるため、デフォルトで scan.completed に集約されます。
- monitor.alert.fired — Unlimited plan で certificate transparency log、DNS record、threat-intelligence database が変化を検知したときに発火します。
- schedule.run.queued — scheduler が再スキャンを queue に入れたときに発火します。CI orchestration に便利です。
Payload 形状
すべての配信は同じ envelope を使います: <code>id</code>、<code>type</code>、<code>created_at</code>、イベント固有の <code>data</code>。
{
"id": "8f1c4e2a-8c3a-4b6f-9c0d-9b1e8f3c2a4d",
"type": "scan.completed",
"created_at": "2026-05-15T10:20:30.000Z",
"data": {
"scan": {
"id": "8f1c4e2a-8c3a-4b6f-9c0d-9b1e8f3c2a4d",
"target_hostname": "staging.example.com",
"mode": "passive",
"status": "completed",
"findings_count": { "critical": 0, "high": 1, "medium": 2, "low": 3, "info": 4 },
"report_url": "https://fixvibe.app/dashboard/scans/8f1c4e2a-..."
}
}
}署名
FixVibe は正確な raw JSON body を HMAC-SHA-256 で署名します。イベントデータを parse したり信頼したりする前に、署名を検証してください。Headers:
fixvibe-signature: t=<timestamp>,v1=<hex hmac>fixvibe-event: scan.completedfixvibe-delivery: <uuid>(idempotency key)
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: string, header: string, secret: string) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const signed = `${parts.t}.${rawBody}`;
const expected = createHmac("sha256", secret).update(signed).digest("hex");
const received = Buffer.from(parts.v1 ?? "", "hex");
const calculated = Buffer.from(expected, "hex");
return received.length === calculated.length && timingSafeEqual(received, calculated);
}リトライ
Any non-2xx response, timeout, DNS failure, or delivery safety rejection is retried with exponential backoff for about 24 hours. Delivery rows show response status, a short response excerpt, attempt count, and dead-letter state in Account → Webhooks.
