Appearance
API Reference
Complete reference for all Canny Bot API endpoints.
Base URL
https://api.canny.bot/api
Authentication
Include one of the following with every request:
API Key (Query Parameter):
?api_key=<your_api_key>
API Key (Header):
x-api-key: <your_api_key>
Widget Endpoints
Generate Widget ID
Create a new widget for your website.
POST /widget/generate
x-api-key: <your_api_key>
Response:
json
{
"data": {
"widgetId": "wgt_123_abc456",
"embedCode": "<script src=\"...\"></script>"
}
}
Get Widget Script
Retrieve the embeddable widget JavaScript.
GET /widget/script/:widget_id
Parameters:
widget_id(path): Widget ID
Response: JavaScript code (Content-Type: application/javascript)
Get Widget Info
Retrieve current widget configuration.
GET /widget/info
x-api-key: <your_api_key>
Response:
json
{
"data": {
"widgetId": "wgt_123_abc456",
"embedCode": "<script src=\"...\"></script>",
"color": "#667eea",
"greeting": "Hello! How can I help you today?",
"borderRadius": 16,
"buttonRadius": 50,
"buttonText": "Chat",
"font": "Arial",
"title": "Chat with us"
}
}
Customize Widget
Update widget appearance and behavior.
PUT /widget/customize
x-api-key: <your_api_key>
Content-Type: application/json
{
"widget_color": "#667eea",
"widget_greeting": "Hello!",
"widget_border_radius": 16,
"widget_button_radius": 50,
"widget_button_text": "Chat",
"widget_font": "Arial",
"widget_title": "Chat with us"
}
Response:
json
{
"data": {
"success": true
}
}
Chat (Non-Streaming)
Send a message to the widget chat.
POST /widget/chat
x-api-key: <your_api_key>
Content-Type: application/json
{
"widgetId": "wgt_123_abc456",
"sessionId": "sess_abc123",
"message": "What is your return policy?"
}
Response:
json
{
"data": {
"response": "Our return policy allows returns within 30 days...",
"sessionId": "sess_abc123"
}
}
Chat Stream
Send a message and receive a streaming response via Server-Sent Events.
POST /widget/chat/stream
Content-Type: application/json
{
"widgetId": "wgt_123_abc456",
"sessionId": "sess_abc123",
"message": "What is your return policy?"
}
Required fields: widgetId, message
Optional fields: sessionId (if omitted, a new session is created)
Response: Content-Type: text/event-stream
data: {"type":"sources","sources":[{"title":"Return Policy","url":"https://..."}]}
data: {"type":"content","content":"Our return "}
data: {"type":"content","content":"policy allows "}
data: {"type":"content","content":"returns within 30 days..."}
data: {"type":"done"}
SSE event types:
| Type | Description |
|---|---|
sources | Source documents used for RAG (sent first, if any) |
content | Incremental response token |
done | Stream complete |
error | Error occurred ({"type":"error","error":"message"}) |
Notes:
- Guard rails are evaluated before generation; if triggered, a single
contentevent with the override/correction is sent - Conversation history (up to 30 messages) is persisted per session
- Monthly interaction limits apply — returns
429when exceeded - Per-session rate limits apply — returns
429withRetry-Afterheader when exceeded
Semantic Search Endpoints
Search
Find documents matching a query.
GET /semantic-search/search?query=<query>&limit=5&threshold=0.1
x-api-key: <your_api_key>
Query Parameters:
query(required): Search querylimit(optional): Max results (default: 5)threshold(optional): Relevance threshold 0-1 (default: 0.1)contentType(optional): Filter by content type
Response:
json
{
"data": [
{
"id": "chunk_123",
"title": "Return Policy",
"textSnippet": "We accept returns within 30 days...",
"fullContent": "Full content...",
"score": 0.87,
"documentId": "doc_1",
"contentType": "page"
}
],
"meta": {
"count": 1
}
}
Answer
Get an AI-generated answer based on search results.
GET /semantic-search/answer?query=<query>&limit=5&threshold=0.1
x-api-key: <your_api_key>
Query Parameters:
query(required): Questionlimit(optional): Source documents to use (default: 5)threshold(optional): Relevance threshold (default: 0.1)contentType(optional): Filter by content type
Response:
json
{
"data": {
"answer": "You can return items within 30 days...",
"sources": [
{
"id": "chunk_123",
"title": "Return Policy",
"textSnippet": "We accept returns within 30 days...",
"score": 0.87
}
]
},
"meta": {
"sourcesCount": 1
}
}
Answer Stream
Get a streaming AI-generated answer.
GET /semantic-search/answer/stream?query=<query>&limit=5&threshold=0.1
x-api-key: <your_api_key>
Response Format (Server-Sent Events):
data: {"type":"sources","sources":[...],"uniqueDocs":[...]}
data: {"type":"content","content":"You can "}
data: {"type":"content","content":"return items..."}
data: {"type":"done"}
Lead Collection Endpoints
Get Lead Collection Config
Retrieve the current lead collection configuration for the authenticated user.
GET /lead-collection/config
x-api-key: <your_api_key>
Response:
json
{
"data": {
"id": 1,
"documentId": "abc123",
"enabled": true,
"webhooks": [
{
"url": "https://hooks.example.com/lead",
"active": true,
"lead_statuses": ["success", "partial"],
"headers": [{ "key": "Authorization", "value": "Bearer token123" }]
}
],
"inactivity_threshold_minutes": 10,
"data_collection_parameters": {
"name": "Extract visitor name",
"email": "Extract email if provided"
}
}
}
Update Lead Collection Config
Update the lead collection configuration.
PUT /lead-collection/config
x-api-key: <your_api_key>
Content-Type: application/json
{
"enabled": true,
"webhooks": [
{
"url": "https://hooks.example.com/lead",
"active": true,
"lead_statuses": ["success", "partial", "failed"],
"headers": [
{ "key": "Authorization", "value": "Bearer token123" }
]
}
],
"inactivity_threshold_minutes": 10,
"data_collection_parameters": {
"name": "Extract visitor name",
"email": "Extract email if provided"
}
}
Webhook Object:
| Field | Type | Description |
|---|---|---|
url | string | Webhook endpoint URL |
active | boolean | Whether this webhook is enabled |
lead_statuses | string[] | Which lead statuses to send: "success", "partial", "failed". Empty = all |
headers | array | Custom HTTP headers. Each header: { key, value, active } |
Response: Same as GET config.
Retry Webhook
Re-send a lead to all active webhooks that match the lead's status.
POST /lead-collection/retry-webhook
x-api-key: <your_api_key>
Content-Type: application/json
{
"lead_id": "abc123"
}
Response:
json
{
"data": {
"id": 1,
"documentId": "abc123",
"status": "success",
"webhook_response": [
{
"url": "https://hooks.example.com/lead",
"success": true,
"status": 200,
"data": { "received": true }
}
],
"sent_at": "2025-07-07T18:30:00.000Z"
}
}
Get Leads (External API)
Retrieve collected leads via API key authentication. Supports filtering by status and pagination.
GET /lead-collection/leads?status=success&limit=50&page=1
x-api-key: <your_api_key>
Query Parameters:
status(optional): Filter by lead status —"success","partial", or"failed"limit(optional): Results per page (default: 50, max: 100)page(optional): Page number (default: 1)
Response:
json
{
"data": [
{
"documentId": "abc123",
"extracted_data": {
"name": "John Doe",
"email": "john@example.com"
},
"chat_summary": "Visitor asked about pricing and provided contact details.",
"status": "success",
"createdAt": "2025-07-07T18:30:00.000Z",
"sent_at": "2025-07-07T18:31:00.000Z"
}
],
"meta": {
"pagination": {
"page": 1,
"pageSize": 50,
"total": 127,
"pageCount": 3
}
}
}
Guard Rails Endpoints
List Guard Rails
GET /guard-rails
x-api-key: <your_api_key>
Response:
json
{
"data": [
{
"id": 1,
"documentId": "abc123",
"name": "No PII",
"description": "Block requests asking for personal information",
"severity": "high",
"correction_prompt": "Please don't share personal information.",
"response_override": null,
"email_contacts": ["admin@example.com"],
"enabled": true,
"trigger_count": 3
}
]
}
Create Guard Rail
POST /guard-rails
x-api-key: <your_api_key>
Content-Type: application/json
{
"name": "No PII",
"description": "Block requests asking for personal information",
"severity": "high",
"correction_prompt": "Please don't share personal information.",
"response_override": null,
"email_contacts": ["admin@example.com"],
"enabled": true
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Guard rail name |
description | string | Yes | What the guard rail detects |
severity | string | No | "low", "medium", "high", "critical" (default: "medium") |
correction_prompt | string | No | Prompt sent to LLM when triggered |
response_override | string | No | Override the entire response when triggered |
email_contacts | string[] | No | Email addresses to alert when triggered |
enabled | boolean | No | Active state (default: true) |
Response: Same as a single guard rail object.
Update Guard Rail
PUT /guard-rails/:id
x-api-key: <your_api_key>
Content-Type: application/json
{
"enabled": false,
"severity": "critical"
}
All fields are optional — only provided fields are updated.
Response: Updated guard rail object.
Delete Guard Rail
DELETE /guard-rails/:id
x-api-key: <your_api_key>
Response:
json
{
"success": true
}
Test Guard Rail
Test a guard rail against sample input using LLM classification.
POST /guard-rails/:id/test
x-api-key: <your_api_key>
Content-Type: application/json
{
"input": "What is your social security number?"
}
Response:
json
{
"data": {
"triggered": true,
"rail": {
"id": "abc123",
"name": "No PII",
"severity": "high"
},
"correction": "Please don't share personal information.",
"override": null
}
}
Agent Script Endpoints
Get Agent Script
Retrieve the current agent script (system prompt) for the authenticated user.
GET /users/script
x-api-key: <your_api_key>
Response:
json
{
"data": {
"script": "You are a helpful assistant for ACME Corp..."
}
}
Update Agent Script
PUT /users/script
x-api-key: <your_api_key>
Content-Type: application/json
{
"script": "You are a helpful assistant for ACME Corp. Always greet users warmly..."
}
Response:
json
{
"success": true,
"script": "You are a helpful assistant for ACME Corp. Always greet users warmly..."
}
Rate Limit Config Endpoints
Get Rate Limit Config
GET /rate-limit/config
x-api-key: <your_api_key>
Response:
json
{
"data": {
"enabled": true,
"max_messages_per_session": 20,
"cooldown_seconds": 60,
"max_messages_per_minute": 5,
"custom_message": ""
}
}
If no config exists, default values are returned.
Update Rate Limit Config
PUT /rate-limit/config
x-api-key: <your_api_key>
Content-Type: application/json
{
"enabled": true,
"max_messages_per_session": 30,
"cooldown_seconds": 90,
"max_messages_per_minute": 8,
"custom_message": "Please wait a moment before sending another message."
}
All fields are optional — only provided fields are updated.
Response: Updated config object.
Analysis Config Endpoints
Get Analysis Config
GET /analysis/config
x-api-key: <your_api_key>
Response:
json
{
"data": {
"enabled": true,
"chat_threshold": 50
}
}
Update Analysis Config
PUT /analysis/config
x-api-key: <your_api_key>
Content-Type: application/json
{
"enabled": true,
"chat_threshold": 100
}
Response: Updated config object.
Trigger Analysis
Manually trigger longitudinal analysis for the authenticated user.
POST /analysis/trigger
x-api-key: <your_api_key>
Content-Type: application/json
{
"language": "en"
}
Response:
json
{
"data": {
"message": "Analysis triggered successfully"
}
}
Account Endpoints
Get Account Status
GET /account/status
x-api-key: <your_api_key>
Response:
json
{
"data": {
"account": "pro",
"subscription": {
"id": "sub_123",
"status": "active",
"currentPeriodEnd": 1720000000
},
"usage": {
"current": 42,
"limit": 1000,
"remaining": 958,
"percentUsed": 4
},
"limits": {
"free": 100,
"pro": 1000,
"enterprise": 10000
}
}
}
Get Interaction History
GET /account/interactions?months=12
x-api-key: <your_api_key>
Query Parameters:
months(optional): Number of months of history to retrieve (default: 12)
Response:
json
{
"data": {
"history": [{ "month": 7, "year": 2025, "amount": 42, "label": "2025-07" }],
"account": "pro",
"limit": 1000
}
}
User Settings Endpoints
Generate API Key
Generate a new API key for the authenticated user.
POST /users/generate-api-key
x-api-key: <your_api_key>
Response:
json
{
"api_key": "sk_a1b2c3d4...",
"message": "API key generated successfully"
}
Revoke API Key
POST /users/revoke-api-key
x-api-key: <your_api_key>
Response:
json
{
"success": true,
"message": "API key revoked successfully"
}
List Available Models
GET /users/models
x-api-key: <your_api_key>
Response:
json
{
"data": [
{ "id": "openai/gpt-4", "name": "GPT-4" },
{ "id": "anthropic/claude-3-opus", "name": "Claude 3 Opus" }
]
}
Save LLM Model
Set the preferred LLM model and provider.
PUT /users/llm-model
x-api-key: <your_api_key>
Content-Type: application/json
{
"llm_model": "openai/gpt-4",
"llm_provider": "openai"
}
Response:
json
{
"success": true,
"llm_model": "openai/gpt-4",
"llm_provider": "openai"
}
Error Responses
400 Bad Request
json
{
"error": "Bad Request",
"message": "Query param is required"
}
401 Unauthorized
json
{
"error": "Unauthorized",
"message": "Authentication required. Use JWT token or api_key parameter"
}
404 Not Found
json
{
"error": "Not Found",
"message": "Widget not found"
}
429 Too Many Requests
json
{
"error": "Rate limit exceeded",
"message": "You have exceeded the rate limit of 10 requests per minute for your free plan.",
"retryAfter": 45
}
500 Internal Server Error
json
{
"error": "Internal Server Error",
"message": "An unexpected error occurred"
}
Rate Limits
Rate limits are enforced per plan:
| Plan | Monthly Limit | Requests/Second |
|---|---|---|
| Free | 100 | 1 |
| Pro | 1,000 | 5 |
| Enterprise | Unlimited | 20 |
Rate Limiting
All authenticated API endpoints are subject to rate limiting based on your plan:
| Plan | Requests/Minute |
|---|---|
| Free | 10 |
| Pro | 100 |
| Enterprise | 1000 |
Rate limit information is included in response headers:
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
X-RateLimit-Reset: 1703341234
Retry-After: 45
When the rate limit is exceeded, the API returns a 429 status code with retry information.
Response Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad Request |
| 401 | Unauthorized |
| 404 | Not Found |
| 429 | Too Many Requests (Rate Limited) |
| 500 | Server Error |