Webhook Delivery
Webhooks push triggered alert events to your server as they fire. This is ideal for headless integrations, backend processing pipelines, and systems that cannot maintain a persistent WebSocket connection.
Setting up a webhook
Add a webhook config when creating a subscription. The url is the endpoint that will receive POST requests, and the optional secret enables HMAC-SHA256 signature verification:
import { Chaos } from '@chaoslabs/ai-sdk';
const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
const sub = await chaos.alerts.subscriptions.create({
alert: {
alert_type: 'price_change',
asset: 'ETH',
target_price: 4000,
condition: 'above',
},
webhook: {
url: 'https://your-server.com/webhooks/alerts',
secret: 'your-signing-secret',
},
});
console.log('Webhook registered:', sub.webhook?.url);Webhook payload
When the alert triggers, Chaos AI sends a POST request to your URL with the triggered alert as a JSON body:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Chaos-Signature | HMAC-SHA256 hex digest (if secret is set) |
{
"id": "ta-60a5a3feae3b",
"userId": "user-123",
"subscriptionId": "ua-d7309261",
"alert": {
"alert_type": "price_change",
"asset": "ETH",
"target_price": 4000,
"condition": "above"
},
"chain": "ethereum",
"severity": "warning",
"title": "ETH price above $4,000",
"summary": "ETH has crossed your $4,000 threshold, currently at $4,125.",
"evidence": {
"dataPoints": [
{ "label": "Current Price", "value": "4125.50", "unit": "USD" },
{ "label": "Threshold", "value": "4000", "unit": "USD" }
],
"chartData": null,
"sourceLinks": null
},
"transactionContext": null,
"readState": "unread",
"archived": false,
"triggeredAt": "2026-03-17T14:30:00Z",
"createdAt": "2026-03-17T14:30:00Z"
}Signature verification
If you provided a secret in the webhook config, every delivery includes an X-Chaos-Signature header containing the HMAC-SHA256 hex digest of the request body.
Verify the signature on your server to ensure the request is authentic:
import { createHmac } from 'crypto';
function verifyWebhookSignature(
body: string,
signature: string,
secret: string
): boolean {
const expected = createHmac('sha256', secret)
.update(body)
.digest('hex');
return signature === expected;
}Express example
import express from 'express';
import { createHmac } from 'crypto';
const app = express();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;
app.post('/webhooks/alerts', express.text({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-chaos-signature'] as string;
const body = req.body as string;
// Verify HMAC signature
const expected = createHmac('sha256', WEBHOOK_SECRET)
.update(body)
.digest('hex');
if (signature !== expected) {
console.error('Invalid webhook signature');
return res.status(401).send('Unauthorized');
}
const alert = JSON.parse(body);
console.log(`[${alert.severity}] ${alert.title}`);
console.log(` ${alert.summary}`);
res.status(200).send('OK');
});
app.listen(3000);Retry policy
Webhook delivery retries failed requests automatically:
| Attempt | Delay |
|---|---|
| 1st | Immediate |
| 2nd | 1 second |
| 3rd | 2 seconds |
| 4th | 4 seconds |
A delivery is considered failed if the HTTP status is 400 or above, or the request times out (10 second timeout). After all retries are exhausted, the delivery is logged as failed.
Managing webhooks
Update or remove the webhook on an existing subscription:
// Update the webhook URL or secret
await chaos.alerts.subscriptions.update('ua-abc123', {
webhook: {
url: 'https://new-endpoint.com/webhooks/alerts',
secret: 'new-secret',
},
});
// Remove the webhook (pass null)
await chaos.alerts.subscriptions.update('ua-abc123', {
webhook: null,
});Webhook vs WebSocket
| Feature | Webhook | WebSocket |
|---|---|---|
| Connection model | Stateless — server pushes to your URL | Persistent — client maintains open connection |
| Latency | Slightly higher (HTTP round-trip) | Lower (already connected) |
| Reliability | Automatic retries on failure | Auto-reconnect on disconnect |
| Authentication | HMAC-SHA256 signature | API key in connection |
| Best for | Backend pipelines, serverless functions | Dashboards, bots, real-time UIs |
You can use both on the same subscription — the alert will be delivered through all configured channels.
Next steps
- Real-time Alerts — WebSocket streaming alternative
- Managing Subscriptions — Full subscription lifecycle
- Alerts API Reference — Full type definitions