Webhook Delivery

Updated

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:

webhook-setup.ts
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:

HeaderValue
Content-Typeapplication/json
X-Chaos-SignatureHMAC-SHA256 hex digest (if secret is set)
webhook-payload.json
{
  "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:

verify-signature.ts
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

express-webhook.ts
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);
[@portabletext/react] Unknown block type "callout", specify a component for it in the `components.types` prop

Retry policy

Webhook delivery retries failed requests automatically:

AttemptDelay
1stImmediate
2nd1 second
3rd2 seconds
4th4 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:

manage-webhooks.ts
// 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

FeatureWebhookWebSocket
Connection modelStateless — server pushes to your URLPersistent — client maintains open connection
LatencySlightly higher (HTTP round-trip)Lower (already connected)
ReliabilityAutomatic retries on failureAuto-reconnect on disconnect
AuthenticationHMAC-SHA256 signatureAPI key in connection
Best forBackend pipelines, serverless functionsDashboards, bots, real-time UIs

You can use both on the same subscription — the alert will be delivered through all configured channels.

Next steps

Was this helpful?