DOCS

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

HeaderDescription
Content-Typeapplication/json
X-HOWLOPS-EventEvent type, e.g. incident.created
X-HOWLOPS-DeliveryUnique delivery UUID for idempotency
X-HOWLOPS-Signature-256HMAC-SHA256 signature (see below)
X-HOWLOPS-TimestampUnix timestamp of the delivery
User-AgentHOWLOPS-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?