Vigil

Webhook payloads

Generic webhook channels receive a JSON POST for every event on the monitors they're attached to. Respond with any 2xx status; failures are recorded on the incident timeline.

Events

  • incident.opened — downtime confirmed
  • incident.resolved — the monitor recovered
  • incident.update — a public update was posted on an incident (status-page subscriber webhooks only)
  • ssl.expiring — certificate inside the warning window
  • monitor.challenged — bot-protection challenge detected (if enabled)
  • monitor.redirect_changed — redirect destination changed (if enabled)

Incident payload

{
  "event": "incident.opened",
  "incidentId": "9b2f6c3a-…",
  "monitorId": "5db41d29-…",
  "monitorName": "Shop",
  "monitorUrl": "https://shop.example.com",
  "cause": "Timeout after 10000ms",
  "httpStatus": null,
  "locationsAffected": ["fra", "nyc"],
  "startedAt": "2026-06-10T14:32:00.000Z",
  "resolvedAt": null
}

incident.resolved sends the same shape with resolvedAt set.

SSL payload

{
  "event": "ssl.expiring",
  "monitorId": "5db41d29-…",
  "monitorName": "Shop",
  "monitorUrl": "https://shop.example.com",
  "issuer": "Let's Encrypt",
  "expiresAt": "2026-06-24T12:00:00.000Z",
  "daysLeft": 14
}

Warning payload

{
  "event": "monitor.redirect_changed",
  "monitorId": "5db41d29-…",
  "monitorName": "Shop",
  "monitorUrl": "https://shop.example.com",
  "detail": "Now redirects to: https://new.example.com (was: https://old.example.com)",
  "at": "2026-06-10T14:32:00.000Z"
}

Verifying signatures

If you set a signing secret on the channel, every request carries an X-Vigil-Signature header: the hex HMAC-SHA256 of the raw request body. Verify it before trusting the payload:

Node.js

import { createHmac, timingSafeEqual } from "crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  return timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}

Status-page subscriber webhooks

Webhook subscribers on a status page receive the same incident payloads (opened and resolved only), unsigned. Treat them as notifications, not authenticated commands.