Webhooks
Webhooks deliver real-time HTTP POST notifications to your server when events occur in Zeodyn™. Use them to trigger CI/CD pipelines, update dashboards, send Slack messages, or integrate with any system that accepts HTTP callbacks.
Creating a webhook
Navigate to Account → Webhooks to create and manage your webhook endpoints. When you create a webhook, you receive a signing secret. Save this secret immediately — it is shown only once and cannot be recovered.
Tier limits
| Tier | Endpoints | Event types |
|---|---|---|
| Free | 0 | — |
| Pro | 1 | score_change |
| Growth | 5 | score_change, scan_complete, threshold_alert, batch_complete |
Event types
score_change
Pro — Fired when a watched site’s Zeodyn Score™ changes after a re-scan.
{
"event": "score_change",
"url": "https://example.com",
"previousScore": 65,
"newScore": 72,
"band": "Strong Foundation",
"resultsUrl": "https://zeodyn.com/results/a1b2c3d4-...",
"timestamp": "2026-02-22T10:30:00.000Z"
}scan_complete
Growth — Fired every time a scan completes for a watched site, regardless of whether the score changed.
{
"event": "scan_complete",
"scanId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"url": "https://example.com",
"score": 72,
"band": "Strong Foundation",
"timestamp": "2026-02-22T10:30:00.000Z"
}threshold_alert
Growth — Fired when a scan result breaches a custom threshold set on a watched site.
{
"event": "threshold_alert",
"url": "https://example.com",
"score": 45,
"threshold": 50,
"direction": "below_min",
"timestamp": "2026-02-22T10:30:00.000Z"
}batch_complete
Growth — Fired when a batch scan finishes processing all URLs.
{
"event": "batch_complete",
"batchId": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
"label": "Monthly check",
"totalUrls": 20,
"completedUrls": 19,
"failedUrls": 1,
"averageScore": 67,
"timestamp": "2026-02-22T10:30:00.000Z"
}Signature verification
Every webhook delivery includes an X-Zeodyn-Signature header containing an HMAC-SHA256 signature of the request body. Always verify this signature before processing the payload.
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post('/webhook', (req, res) => {
const signature = req.headers['x-zeodyn-signature'];
if (!verifyWebhook(req.body, signature, process.env.ZEODYN_WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process the event...
res.status(200).json({ received: true });
});Delivery behaviour
Webhook deliveries are sent as HTTP POST requests with a Content-Type: application/json header. Your endpoint must respond with a 2xx status code within 10 seconds.
Endpoints may be automatically paused after repeated delivery failures. If your endpoint is paused, fix the issue and re-enable it from the webhook management page.
Test delivery
You can send a test event to any webhook endpoint from the management page. Click the play button next to any webhook to send a sample payload and verify your endpoint is working correctly.
score_change webhook to trigger an API call that fetches the updated scan result.