Skip to main content
This example creates a webhook subscription, verifies the signature on each incoming delivery, and handles drift.detected, alert.created, and vulnerability.found events in a Node.js receiver.

Prerequisites

  • A publicly accessible HTTP endpoint (use ngrok for local testing)
export TOKEN="eyJhbGciOi..."
export BASE_URL="https://api.infraaudit.dev"
export RECEIVER_URL="https://receiver.example.com/infraudit"

Step 1: Create the webhook

WEBHOOK=$(curl -s -X POST "$BASE_URL/api/v1/webhooks" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"Production receiver\",
    \"url\": \"$RECEIVER_URL\",
    \"events\": [\"drift.detected\", \"alert.created\", \"vulnerability.found\"],
    \"enabled\": true
  }")

echo $WEBHOOK | jq .

WEBHOOK_ID=$(echo $WEBHOOK | jq -r '.id')
WEBHOOK_SECRET=$(echo $WEBHOOK | jq -r '.secret')

echo "Secret (save this now): $WEBHOOK_SECRET"
The secret is returned only once. Store it immediately in your secrets manager or deployment environment — you cannot retrieve it again.

Step 2: Send a test delivery

curl -s -X POST "$BASE_URL/api/v1/webhooks/$WEBHOOK_ID/test" \
  -H "Authorization: Bearer $TOKEN" | jq .
Your endpoint should receive a ping event.

Step 3: Implement the receiver (Node.js)

// receiver.js
const express = require('express')
const crypto = require('crypto')

const app = express()
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET

function verifySignature(body, signature) {
  if (!signature) return false
  const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET)
  hmac.update(body, 'utf8')
  const expected = 'sha256=' + hmac.digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}

app.post('/infraudit', express.raw({ type: '*/*' }), (req, res) => {
  const signature = req.headers['x-infraaudit-signature']

  if (!verifySignature(req.body, signature)) {
    console.error('Invalid signature')
    return res.status(401).send('Unauthorized')
  }

  const { event, delivery_id, data } = JSON.parse(req.body.toString())
  console.log(`Received ${event} (delivery: ${delivery_id})`)

  switch (event) {
    case 'drift.detected':
      console.log(`Drift on ${data.resource_name}: ${data.summary} (${data.severity})`)
      break
    case 'alert.created':
      console.log(`Alert: ${data.title} (${data.severity})`)
      break
    case 'vulnerability.found':
      console.log(`CVE ${data.cve_id} on ${data.resource_name}`)
      break
    case 'ping':
      console.log('Ping received — endpoint verified')
      break
  }

  res.sendStatus(200)
})

app.listen(3000, () => console.log('Receiver running on :3000'))

Step 4: Check delivery history

curl -s "$BASE_URL/api/v1/webhooks/$WEBHOOK_ID/deliveries" \
  -H "Authorization: Bearer $TOKEN" \
  | jq '.data[] | {event: .event_type, status, response_code}'

Step 5: Local testing with ngrok

If your receiver runs locally:
ngrok http 3000
# Forwarding: https://abc123.ngrok.io -> http://localhost:3000
Use the ngrok URL as RECEIVER_URL when creating the webhook.
For more on signature verification and retry behavior, see Signature Verification and Retries & Delivery.