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.