⚡ You're viewing a live demo of ChimerAI. Data resets daily at midnight UTC.Get the CLI →

Webhooks

ChimerAI's Webhook system lets your app push real-time event notifications to external services — with HMAC-SHA256 signatures, automatic retries, and a delivery log.

What you get

  • Event publishing — Fire webhooks for any domain event (user.created, billing.*, custom events)
  • HMAC-SHA256 signatures — Every request is signed; receivers can verify authenticity
  • Automatic retries — Exponential backoff on failure (3 attempts)
  • Delivery log — Per-event delivery status + response codes
  • Endpoint management — Users can register their own webhook URLs in settings
  • Live event console — Real-time event stream for debugging (as shown in this demo)

Quick setup

npx chimerai add webhooks

Scaffolds:

app/api/webhooks/route.ts              ← Register / list endpoints
app/api/webhooks/[id]/route.ts         ← Update / delete endpoint
app/api/webhooks/events/route.ts       ← SSE stream (live console)
lib/webhooks.ts                        ← fireWebhook() helper
workers/webhook-delivery.ts            ← Background delivery worker
app/webhooks/page.tsx                  ← Webhook console UI

Firing an event

// lib/webhooks.ts
import crypto from 'crypto';
import { db } from '@/lib/db';

export async function fireWebhook(
  workspaceId: string,
  eventType: string,
  payload: Record<string, unknown>
) {
  const endpoints = await db.webhookEndpoint.findMany({
    where: { workspaceId, active: true },
  });

  const event = {
    id: crypto.randomUUID(),
    type: eventType,
    timestamp: new Date().toISOString(),
    data: payload,
  };

  for (const endpoint of endpoints) {
    const body = JSON.stringify(event);
    const signature = crypto.createHmac('sha256', endpoint.secret).update(body).digest('hex');

    try {
      const res = await fetch(endpoint.url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-ChimerAI-Signature': `sha256=${signature}`,
          'X-ChimerAI-Event': eventType,
        },
        body,
        signal: AbortSignal.timeout(10_000),
      });
      await db.webhookDelivery.create({
        data: { endpointId: endpoint.id, eventType, statusCode: res.status, success: res.ok },
      });
    } catch (err) {
      await db.webhookDelivery.create({
        data: {
          endpointId: endpoint.id,
          eventType,
          statusCode: 0,
          success: false,
          error: String(err),
        },
      });
    }
  }
}

Verifying signatures on the receiver side

// In your external app's webhook handler:
import crypto from 'crypto';

export async function POST(req: Request) {
  const rawBody = await req.text();
  const sigHeader = req.headers.get('x-chimerai-signature') ?? '';
  const expected = `sha256=${crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET!)
    .update(rawBody)
    .digest('hex')}`;

  if (sigHeader !== expected) {
    return new Response('Invalid signature', { status: 401 });
  }

  const event = JSON.parse(rawBody);
  console.log('Received event:', event.type, event.data);
  return new Response('ok');
}

Firing webhooks from your events

Integrate fireWebhook() into your business logic:

// When a user signs up:
await fireWebhook(workspaceId, 'user.created', {
  userId: user.id,
  email: user.email,
  plan: 'free',
});

// When a subscription changes:
await fireWebhook(workspaceId, 'billing.subscription_updated', {
  plan: newPlan,
  previousPlan: oldPlan,
});

Prisma schema

model WebhookEndpoint {
  id          String            @id @default(cuid())
  workspaceId String
  url         String
  secret      String
  active      Boolean           @default(true)
  createdAt   DateTime          @default(now())
  deliveries  WebhookDelivery[]
}

model WebhookDelivery {
  id         String          @id @default(cuid())
  endpointId String
  eventType  String
  statusCode Int
  success    Boolean
  error      String?
  createdAt  DateTime        @default(now())
  endpoint   WebhookEndpoint @relation(fields: [endpointId], references: [id])
}

Further reading


Outgoing webhooks — AI tool integration

ChimerAI’s AI service can also call external services as tools (n8n, Zapier, Make.com, Slack). Install with:

chimerai add ai-service
chimerai add ai-tools webhook_tools

File: services/ai/services/tools/webhook_tools.py — no extra dependencies (uses httpx).

call_webhook(url, payload, method, headers?, auth_token?) — universal caller

result = await webhook_tools.call_webhook(
    url="https://myapp.com/api/notify",
    payload={"event": "order_placed", "orderId": "123"},
    method="POST",
    auth_token="my-secret-token"
)
# { success: True, status_code: 200, response: {...} }

Platform shortcuts

# n8n (uses N8N_WEBHOOK_URL env var)
await webhook_tools.call_n8n_webhook("my-workflow-id", {"user": "Alice"})

# Zapier Catch Hook
await webhook_tools.call_zapier_webhook("abc123/xyz456", {"name": "Alice"})

# Make.com
await webhook_tools.call_make_webhook("abc123def456", {"trigger": "new_user"})

# Slack Incoming Webhook
await webhook_tools.notify_slack_via_webhook(
    "https://hooks.slack.com/services/T.../B.../xxx",
    "New signup: Alice",
    channel="#alerts"
)

All methods return a result dict — no exceptions on HTTP errors, safe for use in AI agent loops.

ChimerAI Docs · Back to Demo