Webhook payloads
Envelope format, event catalog, and signature verification for outgoing webhooks.
Webhook payloads
For setup, retry policy, and replay see Integrations → Webhooks. This page is a reference for the payload shapes on the wire.
Envelope
Every webhook request uses the same versioned envelope. data varies per event.
json{ "api_version": "1", "event": "monitor.down", "delivery_id": "whd_8k3nP2oQ1xR7vL9m", "delivered_at": "2026-03-07T14:22:00.812Z", "data": { /* event-specific */ }}
Headers on every request:
| Header | Value |
|---|---|
X-Happyuptime-Event | Public event name |
X-Happyuptime-Delivery | Unique per-attempt delivery id |
X-Happyuptime-Signature | sha256=<hex> HMAC-SHA256 over the raw body |
X-Happyuptime-Delivery-Retry | Present only on manual replays |
Events
monitor.down · monitor.up · monitor.degraded
json{ "monitor": { "id": "mon_abc123", "name": "API", "url": "https://api.example.com/health", "type": "http", "status": "down" }, "check": { "region": "us-east", "regions": null, "status_code": 503, "response_time_ms": 8421, "error": "Service Unavailable" }, "ssl_expiry_days": null, "domain_expiry_days": null, "down_duration": null, "incident": null, "dashboard_url": "https://happyuptime.com/dashboard/monitors/mon_abc123"}
monitor.up includes down_duration (human-readable). monitor.ssl_expiry / monitor.domain_expiry include ssl_expiry_days / domain_expiry_days.
incident.created · incident.updated · incident.acknowledged · incident.resolved
json{ "incident": { "id": "inc_xyz789", "title": "API elevated error rate", "status": "resolved", "severity": "major", "started_at": "2026-03-07T14:12:03Z", "resolved_at": "2026-03-07T14:55:02Z", "acknowledged_at": "2026-03-07T14:18:44Z", "acknowledged_by": "usr_ops1" }, "update": { "id": "upd_001", "status": "resolved", "message": "Root cause: stale cache node. Drained + restarted.", "is_internal": false, "author_id": "usr_ops1", "created_at": "2026-03-07T14:55:02Z" }, "monitors": [{ "id": "mon_abc123", "name": "API", "status": "up" }], "dashboard_url": "https://happyuptime.com/dashboard/incidents/inc_xyz789", "status_page_url": null}
update is null for incident.created and incident.acknowledged unless the user posted an initial message.
vendor.down · vendor.degraded · vendor.resolved
json{ "vendor": { "name": "AWS", "category": "cloud" }, "incident": { "title": "Increased API error rates — US-EAST-1", "severity": "major", "status": "investigating", "source_url": "https://status.aws.amazon.com/" }}
Signature verification
javascriptimport crypto from "crypto";function verify(req, secret) { const header = req.headers["x-happyuptime-signature"]; if (!header) return false; const [algo, hex] = header.split("="); if (algo !== "sha256") return false; const expected = crypto.createHmac("sha256", secret).update(req.rawBody).digest("hex"); const a = Buffer.from(hex); const b = Buffer.from(expected); return a.length === b.length && crypto.timingSafeEqual(a, b);}
Always compare in constant time. Use the raw request body — whitespace/ordering affects the hash.
Idempotency
delivery_id is stable across retries — use it as an idempotency key. Automatic retries reuse the original envelope; manual replays (POST /alerts/log/:id/retry) also keep the same delivery_id.