Overview
DBestChatBot fires outbound webhooks when meaningful events happen on your chatbots (a lead is captured, a conversation starts or ends, a message is sent or received). You configure a destination URL in the dashboard and we POST a JSON envelope to it within seconds of each event.
Webhooks are a Pro / Agency feature. Free-tier accounts can still view conversations and leads in the dashboard but can't pipe them to external systems.
Events
Each webhook subscribes to one or more of these event types:
| Event | When it fires |
|---|---|
lead.captured | A visitor submits the pre-chat form, OR a WhatsApp inbound message starts a conversation (we always have their phone). Recommended for CRM piping. |
conversation.started | A new conversation begins. Useful for marketing analytics or starting follow-up workflows immediately. |
conversation.ended | Visitor closes the chat, or it goes inactive for 5+ minutes. Good for triggering follow-up emails. |
message.received | Visitor sent a message. High volume — only subscribe if you have a specific need. |
message.sent | AI replied to a visitor. High volume — only subscribe if you have a specific need. |
Payload format
Every webhook POSTs a JSON envelope with the same top-level fields. The data object varies by event type. Example for lead.captured:
{
"event": "lead.captured",
"id": "evt_abc123def456",
"created_at": "2026-05-17T15:30:00+00:00",
"data": {
"conversation_id": "conv_xyz789",
"chatbot": {
"id": 42,
"name": "Acme Support"
},
"visitor": {
"name": "Jane Doe",
"email": "jane@acme.com",
"phone": "+15551234567"
},
"source_url": "https://acme.com/pricing",
"message_count": 4,
"transcript_url": "https://dbestchatbot.com/app/conversations/conv_xyz789",
"capture_method": "prechat_form"
}
}Request headers
Each request includes:
Content-Type: application/jsonUser-Agent: DBestChatBot-Webhook/1.0X-DBestChatBot-Event: lead.captured— the event nameX-DBestChatBot-Delivery: evt_abc123— unique delivery ID, use for dedupX-DBestChatBot-Signature: t=1747490400,v1=<hex_hmac>— verification signature
Verifying signatures (recommended)
Always verify webhook payloads to ensure they actually came from DBestChatBot. The signature format is Stripe-compatible: a Unix timestamp t and an HMAC-SHA-256 hex digest v1, joined with a comma.
Algorithm:
- Extract
tandv1from the header - Reject if
tis more than 5 minutes from current time (replay protection) - Compute
expected = HMAC_SHA256(signing_secret, "t.raw_request_body") - Constant-time compare
expectedwithv1 - If they match, the payload is authentic. Otherwise reject.
Node.js / Express
// Node.js / Express example — verify the signature header
import express from 'express';
import crypto from 'crypto';
const app = express();
const SIGNING_SECRET = process.env.DBESTCHATBOT_SIGNING_SECRET;
// IMPORTANT: use raw body, not parsed JSON, for signature verification
app.post('/webhooks/dbestchatbot',
express.raw({ type: 'application/json' }),
(req, res) => {
const header = req.header('X-DBestChatBot-Signature') || '';
const match = header.match(/t=(\d+),v1=([a-f0-9]+)/);
if (!match) return res.status(400).send('bad signature header');
const [, ts, signature] = match;
// 5-minute replay window
if (Math.abs(Date.now() / 1000 - parseInt(ts)) > 300) {
return res.status(400).send('stale timestamp');
}
const expected = crypto
.createHmac('sha256', SIGNING_SECRET)
.update(`${ts}.${req.body}`)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.status(401).send('signature mismatch');
}
const event = JSON.parse(req.body.toString());
console.log('Verified event:', event.event, event.data);
// Always respond 2xx quickly — we'll retry up to 5x with backoff if you don't
res.status(200).send('ok');
},
);PHP
<?php
// PHP example — verify the signature header
$signingSecret = getenv('DBESTCHATBOT_SIGNING_SECRET');
$rawBody = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_DBESTCHATBOT_SIGNATURE'] ?? '';
if (! preg_match('/t=(\d+),v1=([a-f0-9]+)/', $header, $m)) {
http_response_code(400); exit('bad signature header');
}
[, $ts, $signature] = $m;
if (abs(time() - (int) $ts) > 300) {
http_response_code(400); exit('stale timestamp');
}
$expected = hash_hmac('sha256', "{$ts}.{$rawBody}", $signingSecret);
if (! hash_equals($expected, $signature)) {
http_response_code(401); exit('signature mismatch');
}
$event = json_decode($rawBody, true);
// process $event['event'], $event['data'] ...
http_response_code(200);
echo 'ok';Retries and delivery guarantees
We retry up to 5 times with exponential backoff if your endpoint doesn't return a 2xx response within 30 seconds:
| Attempt | Delay from previous |
|---|---|
| 1 | immediate |
| 2 | +1 minute |
| 3 | +5 minutes |
| 4 | +30 minutes |
| 5 | +2 hours |
After the 5th failed attempt, the delivery is marked as failed and we stop retrying. You can see the full delivery log (including HTTP status, error message, and number of attempts) on the webhook detail page in your dashboard.
Always return a 2xx status quickly. If you need to do heavy processing, acknowledge with 200 first and process asynchronously.
Idempotency
Every delivery has a unique id field (also sent as X-DBestChatBot-Delivery). Because we retry on failure, your endpoint may receive the same event more than once. Store the IDs you've already processed and skip duplicates.
IP allowlisting
Outbound webhooks originate from our production servers at 15.204.199.41. If you allowlist by IP, allow this address. Signatures are the more reliable verification path however — IP can spoof, HMAC cannot (without the secret).
Getting started
- Go to Webhooks in your dashboard
- Click Add endpoint — paste your URL, pick events
- Copy the signing secret shown after creation
- Send a test event to verify your endpoint receives and validates it
- Watch real events flow through as visitors use your chatbots
Connecting to Zapier / Make / n8n
All three tools have a generic "Catch Hook" or "Webhook Trigger" action — paste the URL they generate into a new endpoint here, subscribe to the events you want, done. Map fields from data.visitor into your CRM, Sheets, or wherever from there.
HubSpot — full walkthrough
This is the most common setup. Captures every chatbot lead as a HubSpot Contact:
- In Zapier, click Create Zap → Trigger: Webhooks by Zapier → event Catch Hook → continue. Zapier shows you a custom URL like
https://hooks.zapier.com/hooks/catch/12345/abc/ - In DBestChatBot, go to Webhooks → Add endpoint. Paste the Zapier URL, name it "HubSpot via Zapier," subscribe to
lead.captured, save. - Back in Zapier: click Test trigger. Then on your DBestChatBot webhook page, click Send test event. Zapier should receive a sample payload — sample includes
data.visitor.name,data.visitor.email,data.visitor.phone, and any custom pre-chat fields you defined. - In Zapier, add an Action → HubSpot → event Create or Update Contact. Authorize your HubSpot account.
- Map the fields:
- HubSpot
email← Zapierdata.visitor.email - HubSpot
firstname← Zapierdata.visitor.name(split first/last if you need) - HubSpot
phone← Zapierdata.visitor.phone - HubSpot
company← Zapierdata.visitor.company(if you added a custom "Company" field in your chatbot's pre-chat form) - HubSpot
lifecycle_stage→ hardcode to Lead or Subscriber - HubSpot
website← Zapierdata.source_url
- HubSpot
- Optional but recommended: add a second action → HubSpot → Create Note on that contact with the chat transcript URL
data.transcript_urlas the note body. Your sales team clicks the link and sees the full conversation that led to the capture. - Turn the Zap on. Every future
lead.capturedevent auto-creates / updates a HubSpot Contact within seconds.
Pro tip: if you want to route HIGH-VALUE leads differently (e.g. enterprise pricing inquiries) from FAQ-style chats, add a custom field to your pre-chat form like "What are you looking to do?" with options "Just browsing / Buying / Sales question." Then in Zapier add a Filter step that only continues to HubSpot if the answer is "Sales question" — your CRM stays clean.
Other CRMs: the same pattern works for Pipedrive (action: Create Deal), Salesforce (action: Create/Update Lead), Close (action: Create Lead), monday.com (action: Create Item), and Airtable (action: Create Record). The DBestChatBot side is identical — just point a different Zap at the same webhook URL.
FAQ
Why isn't my endpoint receiving events?
- Check the webhook is active in the dashboard
- Check you're subscribed to the right event type
- Check the delivery log on the webhook detail page for HTTP errors
- Use the Send test event button to bypass real-world triggers
How do I stop receiving events temporarily?
Toggle the webhook off — no events are queued or buffered, they're simply dropped.
Can I get older events that already happened?
No, webhooks are fire-and-forget. For historical data, use the dashboard Conversations export (CSV) or contact us about a one-off backfill.