All docs Reference

API + Webhooks

Get real-time HTTPS POSTs to your CRM, Slack, Zapier, n8n, or any other system when events happen on your chatbots.

Outbound webhooks

HMAC SHA-256 signed

Up to 5 retries with backoff

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:

EventWhen it fires
lead.capturedA visitor submits the pre-chat form, OR a WhatsApp inbound message starts a conversation (we always have their phone). Recommended for CRM piping.
conversation.startedA new conversation begins. Useful for marketing analytics or starting follow-up workflows immediately.
conversation.endedVisitor closes the chat, or it goes inactive for 5+ minutes. Good for triggering follow-up emails.
message.receivedVisitor sent a message. High volume — only subscribe if you have a specific need.
message.sentAI 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/json
  • User-Agent: DBestChatBot-Webhook/1.0
  • X-DBestChatBot-Event: lead.captured — the event name
  • X-DBestChatBot-Delivery: evt_abc123 — unique delivery ID, use for dedup
  • X-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:

  1. Extract t and v1 from the header
  2. Reject if t is more than 5 minutes from current time (replay protection)
  3. Compute expected = HMAC_SHA256(signing_secret, "t.raw_request_body")
  4. Constant-time compare expected with v1
  5. 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:

AttemptDelay from previous
1immediate
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

  1. Go to Webhooks in your dashboard
  2. Click Add endpoint — paste your URL, pick events
  3. Copy the signing secret shown after creation
  4. Send a test event to verify your endpoint receives and validates it
  5. 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:

  1. 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/
  2. In DBestChatBot, go to Webhooks Add endpoint. Paste the Zapier URL, name it "HubSpot via Zapier," subscribe to lead.captured, save.
  3. 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.
  4. In Zapier, add an ActionHubSpot → event Create or Update Contact. Authorize your HubSpot account.
  5. Map the fields:
    • HubSpot email ← Zapier data.visitor.email
    • HubSpot firstname ← Zapier data.visitor.name (split first/last if you need)
    • HubSpot phone ← Zapier data.visitor.phone
    • HubSpot company ← Zapier data.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 ← Zapier data.source_url
  6. Optional but recommended: add a second action → HubSpot → Create Note on that contact with the chat transcript URL data.transcript_url as the note body. Your sales team clicks the link and sees the full conversation that led to the capture.
  7. Turn the Zap on. Every future lead.captured event 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.

Ready to wire it up?

Open Webhooks dashboard