The BotPlot API lets you integrate your AI chatbot into any application. Build custom chat interfaces, manage knowledge programmatically, and access analytics data.
https://botplot.app/api/v1Authorization: Bearer YOUR_KEYPublic Key
For chat endpoints. Safe to use in client-side code.
Secret Key
For management endpoints (knowledge, conversations, leads, analytics). Server-side only.
Find your keys in Dashboard → Settings
| Plan | Monthly API Calls |
|---|---|
| Free | No API access |
| Pro | 1,000 / month |
| Business | 5,000 / month |
| Enterprise | 10,000 / month |
Rate limit info is included in response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
{
"error": "Description of the error",
"code": "ERROR_CODE"
}| Status | Name | Description |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or body |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | API access requires a paid plan, or action not allowed |
| 404 | Not Found | Resource not found |
| 429 | Too Many Requests | Monthly API limit exceeded |
| 500 | Internal Error | Something went wrong on our end |
/api/v1/chatPublic KeySend a message and get a response. Creates a new conversation if no conversationId is provided.
| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | Required | The user message to send |
conversationId | string | Optional | Continue an existing conversation |
visitorId | string | Optional | Unique visitor identifier |
curl -X POST "https://botplot.app/api/v1/chat" \
-H "Authorization: Bearer pk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "What is your return policy?"
}'Response
{
"response": "Our return policy allows returns within 30 days of purchase...",
"conversationId": "550e8400-e29b-41d4-a716-446655440000",
"messageId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"sources": 3
}/api/v1/chat/streamPublic KeySend a message and get a streaming SSE response. Tokens arrive in real-time.
| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | Required | The user message to send |
conversationId | string | Optional | Continue an existing conversation |
visitorId | string | Optional | Unique visitor identifier |
curl -X POST "https://botplot.app/api/v1/chat/stream" \
-H "Authorization: Bearer pk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "How do I get started?"
}'Response
data: {"type":"token","content":"To"}
data: {"type":"token","content":" get"}
data: {"type":"token","content":" started"}
data: {"type":"done","messageId":"...","conversationId":"..."}/api/v1/knowledgeSecret KeyList all knowledge sources for the project.
curl -X GET "https://botplot.app/api/v1/knowledge" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"sources": [
{
"id": "550e8400-...",
"type": "url",
"name": "https://example.com",
"status": "ready",
"chunkCount": 12,
"wordCount": 4500,
"syncFrequency": "daily",
"createdAt": "2026-02-15T10:30:00Z"
}
]
}/api/v1/knowledge/urlSecret KeyAdd a URL source. Triggers async crawl, embedding, and indexing.
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Required | The URL to crawl and index |
syncFrequency | string | Optional | "manual", "daily", or "weekly" |
curl -X POST "https://botplot.app/api/v1/knowledge/url" \
-H "Authorization: Bearer sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/docs",
"syncFrequency": "daily"
}'Response
{
"sourceId": "550e8400-...",
"status": "processing"
}/api/v1/knowledge/qaSecret KeyAdd a Q&A pair as a knowledge source.
| Parameter | Type | Required | Description |
|---|---|---|---|
question | string | Required | The question |
answer | string | Required | The answer |
curl -X POST "https://botplot.app/api/v1/knowledge/qa" \
-H "Authorization: Bearer sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"question": "What are your hours?",
"answer": "We are open Monday-Friday, 9am-5pm EST."
}'Response
{
"sourceId": "550e8400-...",
"status": "processing"
}/api/v1/knowledge/:sourceIdSecret KeyGet full details of a knowledge source including its chunks.
| Parameter | Type | Required | Description |
|---|---|---|---|
sourceId | string | Required | The source ID |
curl -X GET "https://botplot.app/api/v1/knowledge/:sourceId" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"source": {
"id": "550e8400-...",
"type": "url",
"name": "https://example.com",
"status": "ready"
},
"chunks": [
{
"id": "...",
"content": "First paragraph...",
"tokenCount": 150
}
]
}/api/v1/knowledge/:sourceId/recrawlSecret KeyRe-crawl a URL source to update its content.
| Parameter | Type | Required | Description |
|---|---|---|---|
sourceId | string | Required | The source ID |
curl -X POST "https://botplot.app/api/v1/knowledge/:sourceId/recrawl" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"success": true,
"status": "processing"
}/api/v1/knowledge/:sourceIdSecret KeyDelete a knowledge source and all its chunks.
| Parameter | Type | Required | Description |
|---|---|---|---|
sourceId | string | Required | The source ID |
curl -X DELETE "https://botplot.app/api/v1/knowledge/:sourceId" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"success": true
}/api/v1/conversationsSecret KeyList conversations with pagination and optional status filter.
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | number | Optional | Max results (default 20, max 100) |
offset | number | Optional | Skip N results |
status | string | Optional | "active", "closed", "archived", or "handed_off" |
curl -X GET "https://botplot.app/api/v1/conversations" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"conversations": [
{
"id": "550e8400-...",
"visitorId": "visitor_abc",
"status": "active",
"overallRating": 1,
"handoffStatus": "none",
"messageCount": 4,
"firstMessage": "Hi, I have a question...",
"createdAt": "2026-02-15T10:30:00Z"
}
],
"total": 42
}/api/v1/conversations/:idSecret KeyGet a conversation with all its messages.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Required | The conversation ID |
curl -X GET "https://botplot.app/api/v1/conversations/:id" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"conversation": {
"id": "550e8400-...",
"status": "active",
"messages": [
{
"id": "...",
"role": "user",
"content": "What is your pricing?",
"createdAt": "2026-02-15T10:30:00Z"
},
{
"id": "...",
"role": "assistant",
"content": "Our pricing starts at...",
"rating": 1,
"createdAt": "2026-02-15T10:30:02Z"
}
]
}
}/api/v1/conversations/:idSecret KeyDelete a conversation and all its messages.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Required | The conversation ID |
curl -X DELETE "https://botplot.app/api/v1/conversations/:id" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"success": true
}/api/v1/leadsSecret KeyList captured leads with pagination and search.
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | number | Optional | Max results (default 20, max 100) |
offset | number | Optional | Skip N results |
search | string | Optional | Search by name or email |
curl -X GET "https://botplot.app/api/v1/leads" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"leads": [
{
"id": "550e8400-...",
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"createdAt": "2026-02-15T10:30:00Z"
}
],
"total": 15
}/api/v1/leads/:idSecret KeyDelete a lead.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Required | The lead ID |
curl -X DELETE "https://botplot.app/api/v1/leads/:id" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"success": true
}/api/v1/analyticsSecret KeyGet project analytics: conversations, messages, visitors, and trends.
| Parameter | Type | Required | Description |
|---|---|---|---|
period | string | Optional | "7d", "30d", or "90d" (default "30d") |
curl -X GET "https://botplot.app/api/v1/analytics" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"overview": {
"totalConversations": 156,
"totalMessages": 892,
"uniqueVisitors": 89,
"avgMessagesPerConversation": 5.7,
"leadsCollected": 23,
"resolutionRate": 94.2
},
"messagesOverTime": [
{
"date": "2026-02-15",
"messages": 12,
"conversations": 5
}
],
"topQuestions": [
{
"question": "What is your pricing?",
"count": 8
}
]
}/api/v1/analytics/ratingsSecret KeyGet customer satisfaction ratings summary and trends.
| Parameter | Type | Required | Description |
|---|---|---|---|
period | string | Optional | "7d", "30d", or "90d" (default "30d") |
curl -X GET "https://botplot.app/api/v1/analytics/ratings" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"summary": {
"totalRatings": 45,
"positive": 38,
"negative": 7,
"satisfactionRate": 84.4
},
"ratingsTrend": [
{
"date": "2026-02-15",
"positive": 3,
"negative": 1
}
]
}/api/v1/projects/:projectId/webhooksSecret KeyList all webhooks for a project.
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | The project ID |
curl -X GET "https://botplot.app/api/v1/projects/:projectId/webhooks" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"webhooks": [
{
"id": "550e8400-...",
"url": "https://example.com/webhooks",
"description": "My integration",
"events": [
"message.created",
"lead.captured"
],
"is_active": true,
"created_at": "2026-02-15T10:30:00Z"
}
]
}/api/v1/projects/:projectId/webhooksSecret KeyCreate a new webhook. Returns the signing secret (shown only once).
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | The project ID |
url | string | Required | HTTPS endpoint URL |
events | string[] | Required | Event types to subscribe to |
description | string | Optional | Friendly description |
curl -X POST "https://botplot.app/api/v1/projects/:projectId/webhooks" \
-H "Authorization: Bearer sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/botplot",
"events": [
"message.created",
"lead.captured"
],
"description": "CRM integration"
}'Response
{
"webhook": {
"id": "550e8400-...",
"url": "https://example.com/webhooks/botplot",
"secret": "whsec_a1b2c3d4e5f6...",
"events": [
"message.created",
"lead.captured"
],
"is_active": true
}
}/api/v1/projects/:projectId/webhooks/:webhookIdSecret KeyDelete a webhook and all its delivery records.
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | The project ID |
webhookId | string | Required | The webhook ID |
curl -X DELETE "https://botplot.app/api/v1/projects/:projectId/webhooks/:webhookId" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"ok": true
}/api/v1/projects/:projectId/webhooks/:webhookId/testSecret KeySend a ping test event. Returns the delivery result immediately.
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | The project ID |
webhookId | string | Required | The webhook ID |
curl -X POST "https://botplot.app/api/v1/projects/:projectId/webhooks/:webhookId/test" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"delivery": {
"deliveryId": "550e8400-...",
"statusCode": 200,
"responseBody": "OK",
"success": true
}
}/api/v1/projects/:projectId/brandingSecret KeyUpdate white-label and custom logo settings. Requires Enterprise plan.
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | The project ID |
white_label | boolean | Optional | Enable/disable white-label (removes BotPlot branding) |
custom_logo | string | Optional | Base64 data URL of logo (PNG/JPG/SVG, max 100KB). Set to null to remove. |
custom_domain | string | Optional | Custom domain (e.g. chat.example.com). Set to null to remove. |
curl -X PATCH "https://botplot.app/api/v1/projects/:projectId/branding" \
-H "Authorization: Bearer sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"white_label": true,
"custom_logo": "data:image/png;base64,iVBOR..."
}'Response
{
"white_label": true,
"custom_logo": true
}/api/v1/projects/:projectId/subdomainSecret KeyConfigure a custom subdomain. Requires Enterprise plan. Must be unique, 3-30 chars, lowercase alphanumeric and hyphens.
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | The project ID |
subdomain | string | Required | Desired subdomain (e.g. my-client-site) |
curl -X PATCH "https://botplot.app/api/v1/projects/:projectId/subdomain" \
-H "Authorization: Bearer sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"subdomain": "my-client-site"
}'Response
{
"subdomain": "my-client-site"
}/api/v1/projects/:projectId/verify-domainSecret KeyVerify DNS configuration for a custom domain. Checks CNAME record resolution.
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | The project ID |
curl -X POST "https://botplot.app/api/v1/projects/:projectId/verify-domain" \
-H "Authorization: Bearer sk_YOUR_KEY"Response
{
"verified": true,
"domain": "chat.example.com"
}Every webhook delivery includes an X-BotPlot-Signature header. Verify this signature to ensure the payload was sent by BotPlot and hasn't been tampered with.
| Header | Description |
|---|---|
X-BotPlot-Signature | HMAC-SHA256 signature: sha256=<hex> |
X-BotPlot-Event | The event type (e.g., message.created) |
X-BotPlot-Delivery | Unique delivery ID for deduplication |
User-Agent | BotPlot-Webhooks/1.0 |
{
"event": "message.created",
"project_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-02-15T10:30:00.000Z",
"data": {
"conversation_id": "...",
"message_id": "...",
"role": "assistant",
"content": "Here is the answer to your question...",
"confidence_score": 0.87,
"created_at": "2026-02-15T10:30:00.000Z"
}
}| Event | Description |
|---|---|
conversation.started | A new conversation was started |
conversation.ended | A conversation was idle for 30+ minutes and closed |
message.created | A user or assistant message was created |
lead.captured | A visitor submitted their contact info |
handoff.requested | A visitor requested human support |
rating.received | A message or conversation was rated |
Node.js
const crypto = require("crypto");
function verifyWebhook(payload, signature, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express example
app.post("/webhooks/botplot", (req, res) => {
const signature = req.headers["x-botplot-signature"];
const isValid = verifyWebhook(
JSON.stringify(req.body),
signature,
"whsec_YOUR_SECRET"
);
if (!isValid) return res.status(401).send("Invalid signature");
const event = req.headers["x-botplot-event"];
console.log("Received event:", event, req.body);
res.status(200).send("OK");
});Python
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route("/webhooks/botplot", methods=["POST"])
def handle_webhook():
signature = request.headers.get("X-BotPlot-Signature", "")
is_valid = verify_webhook(
request.data,
signature,
"whsec_YOUR_SECRET"
)
if not is_valid:
return "Invalid signature", 401
event = request.headers.get("X-BotPlot-Event")
print(f"Received event: {event}", request.json)
return "OK", 200cURL (testing)
# Generate a test signature
SECRET="whsec_YOUR_SECRET"
PAYLOAD='{"event":"ping","project_id":"...","timestamp":"...","data":{}}'
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/.* /sha256=/')
# Verify it matches X-BotPlot-Signature header
echo "Signature: $SIGNATURE"