Webhooks
Receive real-time event notifications from Chatsby AI — setup, event types, payload formats, signature verification, retry logic, and best practices.
Webhooks let your application react to events in real time. Instead of polling the API for changes, Chatsby sends HTTP POST requests to your server whenever something happens — a new conversation starts, a message is sent, a source finishes training, or a lead is captured.
When to Use Webhooks
Webhooks are the right choice when you need to:
- Sync data in real time — Push new conversations into your CRM, helpdesk, or analytics platform as they happen.
- Trigger automated workflows — Create a support ticket when a conversation starts, send a Slack notification when a lead is captured, or retrain an agent when a source update completes.
- Update your UI — Notify your dashboard or admin panel that new data is available without constant polling.
- Build event-driven architectures — Decouple your systems so they react to events rather than synchronously querying the API.
Setting Up Webhooks
Open the Webhooks page
Log in to the Chatsby Dashboard and navigate to Settings > Webhooks.
Add an endpoint
Click Add Endpoint. Enter the URL of your server that will receive webhook payloads. This must be an HTTPS URL that is publicly accessible.
Select events
Choose which events should trigger deliveries to this endpoint. You can subscribe to all events or select specific ones.
Save and copy the signing secret
After saving, Chatsby generates a unique signing secret for this endpoint (whsec_...). Copy it immediately — you will use it to verify webhook signatures. The full secret is only shown once.
Verify your endpoint
Chatsby sends a test ping event to confirm your endpoint is reachable. Your server must respond with a 200 status code within 10 seconds.
Available Events
| Event | Description | Triggered when |
|---|---|---|
conversation.created | A new conversation has started. | A user sends their first message to an agent. |
message.created | A new message was added to a conversation. | A user sends a message or the agent responds. |
source.trained | A source has finished processing successfully. | A source transitions to trained status. |
source.failed | A source failed to process. | A source transitions to failed status. |
action.triggered | A custom action was triggered during a conversation. | The agent invokes a configured action (e.g., "book a meeting"). |
lead.captured | Lead information was collected during a conversation. | A user provides their name, email, or other contact info. |
Payload Format
Every webhook delivery is an HTTP POST request with a JSON body. All payloads share a common envelope structure:
{
"id": "evt_abc123def456",
"event": "event.type",
"created_at": "2025-03-15T14:30:00Z",
"data": {
// Event-specific payload
}
}| Field | Type | Description |
|---|---|---|
id | string | Unique event identifier. Use this for idempotency checks. |
event | string | The event type (e.g., conversation.created). |
created_at | string | ISO 8601 timestamp of when the event occurred. |
data | object | Event-specific data (varies by event type). |
conversation.created
{
"id": "evt_conv_001",
"event": "conversation.created",
"created_at": "2025-03-15T14:30:00Z",
"data": {
"id": "conv_xyz789abcd",
"agent_id": "agent_1a2b3c4d5e",
"metadata": {
"user_id": "usr_12345",
"source_page": "/pricing"
},
"created_at": "2025-03-15T14:30:00Z"
}
}message.created
{
"id": "evt_msg_001",
"event": "message.created",
"created_at": "2025-03-15T14:30:03Z",
"data": {
"conversation_id": "conv_xyz789abcd",
"agent_id": "agent_1a2b3c4d5e",
"message": {
"role": "assistant",
"content": "Our return policy allows returns within 30 days of purchase.",
"sources_used": [
{ "id": "src_abc123defg", "title": "Return Policy Page" }
],
"timestamp": "2025-03-15T14:30:03Z"
}
}
}source.trained
{
"id": "evt_src_001",
"event": "source.trained",
"created_at": "2025-03-15T12:05:00Z",
"data": {
"id": "src_xyz456abcd",
"type": "website",
"content": "https://acme.com/help",
"status": "trained",
"character_count": 31200,
"agent_id": "agent_1a2b3c4d5e",
"created_at": "2025-03-15T12:00:00Z"
}
}action.triggered
{
"id": "evt_act_001",
"event": "action.triggered",
"created_at": "2025-03-15T14:35:00Z",
"data": {
"conversation_id": "conv_xyz789abcd",
"agent_id": "agent_1a2b3c4d5e",
"action": {
"name": "book_meeting",
"parameters": {
"date": "2025-03-20",
"time": "14:00",
"timezone": "America/New_York"
}
}
}
}lead.captured
{
"id": "evt_lead_001",
"event": "lead.captured",
"created_at": "2025-03-15T14:33:00Z",
"data": {
"conversation_id": "conv_xyz789abcd",
"agent_id": "agent_1a2b3c4d5e",
"lead": {
"name": "Jane Smith",
"email": "[email protected]",
"phone": "+1-555-0123",
"company": "Example Inc."
}
}
}Webhook Security — Signature Verification
Every webhook delivery includes an X-Chatsby-Signature header containing an HMAC-SHA256 signature of the request body. Always verify this signature before processing the payload to ensure the request came from Chatsby and was not tampered with.
The signature is computed as:
HMAC-SHA256(signing_secret, raw_request_body)
Verification in Node.js
import crypto from 'crypto';
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
// In your Express route handler:
app.post('/webhooks/chatsby', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-chatsby-signature'];
const isValid = verifyWebhookSignature(
req.body.toString(),
signature,
process.env.CHATSBY_WEBHOOK_SECRET
);
if (!isValid) {
console.error('Invalid webhook signature');
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Process the event...
res.status(200).send('OK');
});Verification in Python
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# In your Flask route handler:
@app.route('/webhooks/chatsby', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Chatsby-Signature')
if not verify_webhook_signature(request.data, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.get_json()
# Process the event...
return 'OK', 200Always use a timing-safe comparison function (crypto.timingSafeEqual in Node.js, hmac.compare_digest in Python) to prevent timing attacks.
Retry Logic
If your endpoint does not respond with a 2xx status code within 10 seconds, Chatsby retries the delivery with exponential backoff:
| Attempt | Delay after failure |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 2 minutes |
| 3rd retry | 10 minutes |
| 4th retry | 1 hour |
| 5th retry | 6 hours |
After 5 failed retries, the delivery is marked as permanently failed. You can view failed deliveries and manually retry them from the Settings > Webhooks page in your dashboard.
If an endpoint consistently fails (10+ consecutive failures), Chatsby automatically disables it and sends a notification email to the account owner. Re-enable it from the dashboard after fixing the issue.
Testing Webhooks
During development, your local server is not publicly accessible. Use one of these tools to expose it:
Using ngrok
# Start your local server on port 3000
npm run dev
# In another terminal, expose it with ngrok
ngrok http 3000ngrok gives you a public HTTPS URL (e.g., https://abc123.ngrok.io) that you can enter as your webhook endpoint in the Chatsby dashboard.
Using webhook.site
webhook.site provides a temporary URL that captures and displays incoming requests. Use it to inspect payload formats without writing any code.
Sending test events
From the Settings > Webhooks page in your dashboard, click Send Test Event next to any configured endpoint. This delivers a sample payload for each subscribed event type so you can verify your handler works correctly.
Debugging Failed Webhooks
When webhook deliveries fail, check these common issues:
| Problem | Symptom | Fix |
|---|---|---|
| Endpoint not reachable | All deliveries fail immediately | Verify the URL is correct and publicly accessible over HTTPS. |
| Timeout | Deliveries fail after 10 seconds | Your handler is too slow. Acknowledge the webhook immediately and process asynchronously. |
| Invalid SSL certificate | Connection refused | Ensure your server has a valid, non-expired SSL certificate. Self-signed certs are not accepted. |
| Signature verification failing | Your handler returns 401 | Ensure you are using the raw request body (not parsed JSON) for signature verification. |
| 500 errors from your handler | Intermittent failures | Check your application logs for exceptions in the webhook handler. |
View delivery logs for each endpoint in your dashboard under Settings > Webhooks > [Endpoint] > Delivery History. Each log entry shows the request payload, response status code, response body, and latency.
Best Practices
Respond quickly. Return a 200 status code within 1-2 seconds. If your processing takes longer, acknowledge the webhook immediately and handle the work asynchronously (e.g., push to a job queue).
app.post('/webhooks/chatsby', (req, res) => {
// Acknowledge immediately
res.status(200).send('OK');
// Process asynchronously
processWebhookAsync(req.body).catch(console.error);
});Be idempotent. Webhooks may be delivered more than once (network retries, server restarts). Use the event id field to deduplicate. Store processed event IDs and skip events you have already handled.
Handle events out of order. Events may arrive in a different order than they occurred. Use the created_at timestamp to determine the true sequence if order matters to your logic.
Log everything. Store the full webhook payload for debugging. When something goes wrong, having the raw data makes diagnosis significantly faster.
Monitor your endpoint. Set up alerts for elevated failure rates. A sudden spike in webhook failures often indicates a deployment issue or infrastructure problem on your end.
On this page
- When to Use Webhooks
- Setting Up Webhooks
- Available Events
- Payload Format
- conversation.created
- message.created
- source.trained
- action.triggered
- lead.captured
- Webhook Security — Signature Verification
- Verification in Node.js
- Verification in Python
- Retry Logic
- Testing Webhooks
- Using ngrok
- Using webhook.site
- Sending test events
- Debugging Failed Webhooks
- Best Practices