ChatbotReply

API reference

REST API v1 and outgoing webhooks · Last updated: October 14, 2026

Authentication

Create a secret API key in your dashboard under Security → API keys, and give it only the scopes it needs. Send it on every request:

Authorization: Bearer sk_live_…

Base URL: https://chatbotreply.com/api/v1. Keep secret keys on your server. The public widget key (cb_…) is embedded in websites and cannot use this API.

A key from another region gets 421 with the API address to call instead.

Scopes

ScopeAllows
conversations:readList conversations and read their messages
conversations:writeReply in a conversation as a person; send customer messages from your own channel
tickets:read / tickets:writeRead / create and update tickets
knowledge:read / knowledge:writeRead FAQs / add FAQs and documents
leads:readRead captured leads
analytics:readRead metrics and agent versions
orders:writeSend your orders, so the agent can answer order questions

Limits and retries

  • Rate limit: 120 requests per minute per key and IP address. Above that you get 429; wait a minute and retry.
  • Safe retries: send an Idempotency-Key header (any unique string, up to 100 characters) on POST requests. A retry with the same key returns the stored response instead of doing the work twice. While the first request is still running, a retry gets 409.

Endpoints

All bodies and responses are JSON. Conversations created by your own test runs (QA Lab) are left out of lists.

RequestScopeWhat it does
GET /conversations?limit=50conversations:readLatest conversations (limit 1–200)
GET /conversations/{id}conversations:readOne conversation, with its handoff card if a person was asked for
GET /conversations/{id}/messages?limit=50conversations:readIts messages (limit 1–200)
POST /conversations/{id}/messagesconversations:writeReply as a person: { "text": "…" } (up to 2,000 characters). Sent on the customer's channel (WhatsApp, Messenger…) or shown in the website widget; the bot stays quiet in that conversation. Returns 201 { ok, message_id, delivered_via }
POST /messagesconversations:writeYour own channel: send a customer's message and get the agent's reply. Body { "text": "…", "session_id"?: "…", "visitor_label"?: "…" } (text up to 4,000 characters). Returns { sessionId, reply, type, confidence, suggestions, sources, … }; pass sessionId back as session_id to continue the conversation
GET /tickets?status=opentickets:readTickets, optionally by status
POST /ticketstickets:writeCreate: { "subject": "…", "session_id"?: "…", "priority"?: "low" | "normal" | "high" | "urgent" } → 201
PATCH /tickets/{id}tickets:writeUpdate any of status (open, pending, resolved, closed), priority, assignee, subject
GET /knowledge/faqsknowledge:readAll FAQs
POST /knowledge/faqsknowledge:writeAdd: { "question": "…", "answer": "…", "keywords"?: "…" } → 201
POST /knowledge/documentsknowledge:writeAdd or update a document the agent can answer from: { "title": "…", "text": "…", "url"?: "https://…" } → 201 { ok, source, stored, changed }. The same URL (or title) replaces the earlier version
GET /leadsleads:readCaptured leads
GET /analytics?days=30analytics:readThe dashboard's metrics for the last 1–365 days
GET /agentsanalytics:readAgent versions: id, version, status, rollout and publish time
POST /ordersorders:writeAdd or update up to 500 orders: { "orders": [{ "order_number": "…", "status": "…", "eta"?, "carrier"?, "tracking_number"?, "items"?, "customer_email"?, "customer_phone"?, "total"?, "currency"? }] } → { ok, upserted }. Orders without a number or status are skipped

Example

curl https://chatbotreply.com/api/v1/messages \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2e" \
  -d '{"text":"Do you ship to Canada?","visitor_label":"in-app chat"}'

Errors

Errors are JSON: { "error": "what went wrong" }.

  • 400: missing or invalid fields.
  • 401: missing, invalid or revoked key, or the public widget key.
  • 403: the key lacks the scope.
  • 404: not found, or not your business's.
  • 409: the same Idempotency-Key is still in progress.
  • 421: wrong region.
  • 429: rate limit.

Outgoing webhooks

Add endpoints in your dashboard under Integrations → Outgoing webhooks, and choose which events each one gets (none chosen = all). The dashboard shows each endpoint's latest result, a delivery log, and a Send test event button.

  • Addresses: endpoint URLs must be public https:// addresses on the standard port (443); private and internal addresses are refused. Adding endpoints and sending test events are limited to 20 a minute.
  • Signing secret: shown once, when you add the endpoint.

Request

Each event is a POST with a JSON body:

{ "id": "3b1f…", "event": "lead.created", "business_id": "biz_…", "occurred_at": "2026-10-14T12:00:00.000Z", "data": { … } }
HeaderValue
X-Webhook-IdThe delivery id (also id in the body). It is the same on every retry: use it to ignore duplicates
X-Webhook-EventThe event name
X-Webhook-TimestampWhen it was sent, in milliseconds since 1970
X-Webhook-SignatureHex HMAC-SHA256 of timestamp + "." + raw body, keyed with the signing secret

Check the signature (Node.js)

const crypto = require('crypto');
function isFromChatbotReply(secret, headers, rawBody) {
  const ts = headers['x-webhook-timestamp'];
  if (Math.abs(Date.now() - Number(ts)) > 5 * 60 * 1000) return false; // too old: possible replay
  const expected = crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
  const got = String(headers['x-webhook-signature'] || '');
  return got.length === expected.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
}

Check against the raw request body, before parsing it as JSON.

Retries

Answer with any 2xx status within 10 seconds. Anything else, a timeout or a network error is retried. There are up to 5 attempts, after waits of 30 seconds, 1, 2 and 4 minutes, so about 8 minutes in all. Every attempt shows in the dashboard's delivery log.

Events

  • Conversations: conversation.started, conversation.resolved, conversation.escalated.
  • Messages: message.received, message.sent.
  • Handoffs and tickets: handoff.requested, ticket.created, ticket.updated.
  • Leads: lead.captured, lead.created.
  • Actions: action.executed, action.started, action.completed, action.failed, action.approval_requested.
  • Knowledge and agent: knowledge.updated, agent.deployed.
  • Test: the dashboard's test button sends webhook.test.