Webhooks reference
Outbound webhook payloads, delivery headers, signature verification, and retry behavior.
HOWLOPS can deliver real-time event notifications to an HTTP endpoint you control. Configure a webhook notification channel under Settings > Notification channels > Webhook.
Delivery
- Method:
POST - Content-Type:
application/json - Timeout: 10 seconds per delivery attempt
- Retries: up to 3 attempts with exponential back-off (5 s, 30 s, 5 min)
Delivery headers
| Header | Description |
|---|---|
Content-Type | application/json |
X-HOWLOPS-Event | Event type, e.g. incident.created |
X-HOWLOPS-Delivery | Unique delivery UUID for idempotency |
X-HOWLOPS-Signature-256 | HMAC-SHA256 signature (see below) |
X-HOWLOPS-Timestamp | Unix timestamp of the delivery |
User-Agent | HOWLOPS-Webhook/1.0 |
Signature verification
Compute the HMAC-SHA256 of the raw request body using your webhook secret as the key. Compare it to the X-HOWLOPS-Signature-256 header value (prefixed with sha256=).
import hmac
import hashlib
def verify(secret: str, body: bytes, header: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header)
import crypto from "node:crypto";
function verify(secret: string, body: Buffer, header: string): boolean {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(body).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}
Always use a constant-time comparison to prevent timing attacks.
Event catalog
incident.created
Fired when a new incident is opened.
{
"event": "incident.created",
"delivery_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-05-13T10:00:00Z",
"data": {
"incident_id": "uuid",
"monitor_id": "uuid",
"monitor_name": "Production API",
"severity": "critical",
"status": "open",
"started_at": "2026-05-13T09:59:50Z",
"check_url": "https://api.example.com/health"
}
}
incident.acknowledged
Fired when a team member acknowledges an incident.
{
"event": "incident.acknowledged",
"delivery_id": "...",
"timestamp": "2026-05-13T10:05:00Z",
"data": {
"incident_id": "uuid",
"monitor_name": "Production API",
"acknowledged_by": "[email protected]",
"acknowledged_at": "2026-05-13T10:05:00Z"
}
}
incident.resolved
Fired when an incident is resolved (manually or by recovery detection).
{
"event": "incident.resolved",
"delivery_id": "...",
"timestamp": "2026-05-13T10:20:00Z",
"data": {
"incident_id": "uuid",
"monitor_name": "Production API",
"duration_seconds": 1210,
"resolved_at": "2026-05-13T10:20:00Z",
"resolved_by": "auto"
}
}
monitor.status_changed
Fired when a monitor transitions between up, down, or paused.
{
"event": "monitor.status_changed",
"delivery_id": "...",
"timestamp": "2026-05-13T10:00:00Z",
"data": {
"monitor_id": "uuid",
"monitor_name": "Production API",
"previous_status": "up",
"current_status": "down",
"changed_at": "2026-05-13T10:00:00Z"
}
}
heartbeat.missed
Fired when a heartbeat does not receive a ping within the grace period.
{
"event": "heartbeat.missed",
"delivery_id": "...",
"timestamp": "2026-05-13T03:05:00Z",
"data": {
"heartbeat_id": "uuid",
"heartbeat_name": "Nightly backup",
"expected_at": "2026-05-13T03:00:00Z",
"grace_seconds": 300
}
}
maintenance.started / maintenance.ended
{
"event": "maintenance.started",
"delivery_id": "...",
"timestamp": "2026-05-13T02:00:00Z",
"data": {
"window_id": "uuid",
"name": "DB upgrade",
"affected_monitors": ["uuid-1", "uuid-2"],
"ends_at": "2026-05-13T04:00:00Z"
}
}
Testing webhooks
Send a test delivery from Settings > Notification channels > select channel > Test. The test event uses event: "test" with a fixed sample payload.
For local development, use a tool such as Smee.io (no HOWLOPS affiliation) to forward webhook deliveries to localhost.
See also
Was this page helpful?