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
- Webhook Guide — complete reference for both outgoing AI-tool webhooks and Stripe incoming webhook handling
- HMAC authentication
- ChimerAI Audit Log
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.