Skip to content
On this page

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:

TypeDescription
sourcesSource documents used for RAG (sent first, if any)
contentIncremental response token
doneStream complete
errorError occurred ({"type":"error","error":"message"})

Notes:

  • Guard rails are evaluated before generation; if triggered, a single content event with the override/correction is sent
  • Conversation history (up to 30 messages) is persisted per session
  • Monthly interaction limits apply — returns 429 when exceeded
  • Per-session rate limits apply — returns 429 with Retry-After header when exceeded

Semantic Search Endpoints

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 query
  • limit (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): Question
  • limit (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:

FieldTypeDescription
urlstringWebhook endpoint URL
activebooleanWhether this webhook is enabled
lead_statusesstring[]Which lead statuses to send: "success", "partial", "failed". Empty = all
headersarrayCustom 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:

FieldTypeRequiredDescription
namestringYesGuard rail name
descriptionstringYesWhat the guard rail detects
severitystringNo"low", "medium", "high", "critical" (default: "medium")
correction_promptstringNoPrompt sent to LLM when triggered
response_overridestringNoOverride the entire response when triggered
email_contactsstring[]NoEmail addresses to alert when triggered
enabledbooleanNoActive 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:

PlanMonthly LimitRequests/Second
Free1001
Pro1,0005
EnterpriseUnlimited20

Rate Limiting

All authenticated API endpoints are subject to rate limiting based on your plan:

PlanRequests/Minute
Free10
Pro100
Enterprise1000

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

CodeMeaning
200Success
400Bad Request
401Unauthorized
404Not Found
429Too Many Requests (Rate Limited)
500Server Error