Advanced Guides

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

EventDescriptionTriggered when
conversation.createdA new conversation has started.A user sends their first message to an agent.
message.createdA new message was added to a conversation.A user sends a message or the agent responds.
source.trainedA source has finished processing successfully.A source transitions to trained status.
source.failedA source failed to process.A source transitions to failed status.
action.triggeredA custom action was triggered during a conversation.The agent invokes a configured action (e.g., "book a meeting").
lead.capturedLead 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
  }
}
FieldTypeDescription
idstringUnique event identifier. Use this for idempotency checks.
eventstringThe event type (e.g., conversation.created).
created_atstringISO 8601 timestamp of when the event occurred.
dataobjectEvent-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', 200

Always 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:

AttemptDelay after failure
1st retry30 seconds
2nd retry2 minutes
3rd retry10 minutes
4th retry1 hour
5th retry6 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 3000

ngrok 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:

ProblemSymptomFix
Endpoint not reachableAll deliveries fail immediatelyVerify the URL is correct and publicly accessible over HTTPS.
TimeoutDeliveries fail after 10 secondsYour handler is too slow. Acknowledge the webhook immediately and process asynchronously.
Invalid SSL certificateConnection refusedEnsure your server has a valid, non-expired SSL certificate. Self-signed certs are not accepted.
Signature verification failingYour handler returns 401Ensure you are using the raw request body (not parsed JSON) for signature verification.
500 errors from your handlerIntermittent failuresCheck 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.