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:

HeaderValue
X-Happyuptime-EventPublic event name
X-Happyuptime-DeliveryUnique per-attempt delivery id
X-Happyuptime-Signaturesha256=<hex> HMAC-SHA256 over the raw body
X-Happyuptime-Delivery-RetryPresent 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

javascript
import 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.