Webhooks

HLD delivers real-time event notifications to your endpoint via HTTP POST. Subscribe to alert state changes, incident lifecycle events, compliance triggers, and more.

Register a webhook endpoint

bash
POST /v1/webhooks

{
  "url": "https://your-system.com/hld-webhook",
  "events": ["alert.created", "alert.severity_changed", "incident.created", "incident.contained"],
  "secret": "your-signing-secret"
}
json
{
  "id": "wh_01hxyz",
  "url": "https://your-system.com/hld-webhook",
  "events": ["alert.created", "alert.severity_changed", "incident.created", "incident.contained"],
  "status": "active",
  "created_at": "2025-06-01T09:00:00Z"
}

Webhook payload shape

Every webhook delivery follows the same envelope structure.

json
{
  "id": "evt_01hxyz",
  "type": "alert.created",
  "created_at": "2025-06-01T03:14:00Z",
  "tenant_id": "ten_01hxyz",
  "data": {
    // Full resource object for the event type
  }
}

Retry behaviour

HLD retries failed deliveries with exponential backoff — 5s, 30s, 5m, 30m, 2h. If your endpoint returns a non-2xx status or times out (10s), it is considered a failure. After 5 failed attempts the delivery is abandoned and the endpoint is marked degraded.

Tip:Respond with a 200 immediately upon receipt, then process the event asynchronously. Long-running processing in the request handler causes timeouts.

Signature verification

Every webhook delivery includes an X-HLD-Signature-256 header containing an HMAC-SHA256 signature of the raw request body, signed with your endpoint secret.

typescript
import crypto from 'crypto'

function verifySignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  )
}

// In your handler:
const sig = req.headers['x-hld-signature-256']
const body = req.rawBody // unparsed string
if (!verifySignature(body, sig, process.env.HLD_WEBHOOK_SECRET)) {
  return res.status(401).send('Invalid signature')
}