Overview
Integrations connect a chatbot to an external messaging channel or tool server. Supported types:
| Channel | Type value | Description |
|---|---|---|
whatsapp | WhatsApp Business API via Meta | |
| REST API | rest-api | Generic REST webhook for custom integrations |
| Web Widget | web | Embeddable chat widget for websites |
| MCP Server | mcp | Remote tool servers for LLM agents (Model Context Protocol) |
List Integrations
Returns all integrations attached to a chatbot.
GET /api/dev/v1/chatbots/{chatbot_id}/integrations Authorization: Bearer <api-key>GET /api/dev/v1/chatbots/{chatbot_id}/integrations Authorization: Bearer <api-key>
curl -X GET http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations \ -H "Authorization: Bearer <your-api-key>"curl -X GET http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations \ -H "Authorization: Bearer <your-api-key>"
Response - 200 OK
{ "items": [ { "id": "01JST...", "chatbot_id": "01JMXYZ...", "type": "whatsapp", "is_active": true, "created_at": "2026-01-25T11:00:00Z" } ], "total": 1 }{ "items": [ { "id": "01JST...", "chatbot_id": "01JMXYZ...", "type": "whatsapp", "is_active": true, "created_at": "2026-01-25T11:00:00Z" } ], "total": 1 }
Get Integration
GET /api/dev/v1/chatbots/{chatbot_id}/integrations/{integration_id} Authorization: Bearer <api-key>GET /api/dev/v1/chatbots/{chatbot_id}/integrations/{integration_id} Authorization: Bearer <api-key>
curl -X GET http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/<integration-id> \ -H "Authorization: Bearer <your-api-key>"curl -X GET http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/<integration-id> \ -H "Authorization: Bearer <your-api-key>"
Delete Integration
Permanently removes an integration. Users on this channel will no longer be able to reach the chatbot.
DELETE /api/dev/v1/chatbots/{chatbot_id}/integrations/{integration_id} Authorization: Bearer <api-key>DELETE /api/dev/v1/chatbots/{chatbot_id}/integrations/{integration_id} Authorization: Bearer <api-key>
curl -X DELETE http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/<integration-id> \ -H "Authorization: Bearer <your-api-key>"curl -X DELETE http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/<integration-id> \ -H "Authorization: Bearer <your-api-key>"
Response - 204 No Content
WhatsApp Integration
Create WhatsApp Integration
POST /api/dev/v1/chatbots/{chatbot_id}/integrations/whatsapp Authorization: Bearer <api-key> Content-Type: application/jsonPOST /api/dev/v1/chatbots/{chatbot_id}/integrations/whatsapp Authorization: Bearer <api-key> Content-Type: application/json
Body
phone_number_idstringrequiredMeta WhatsApp Business phone number ID.
access_tokenstringrequiredPermanent access token from Meta for Business.
verify_tokenstringrequiredToken you set when configuring the Meta webhook.
curl -X POST http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/whatsapp \ -H "Authorization: Bearer <your-api-key>" \ -H "Content-Type: application/json" \ -d '{ "phone_number_id": "1234567890", "access_token": "EAABwzLixnjYBO...", "verify_token": "my-webhook-verify-token" }'curl -X POST http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/whatsapp \ -H "Authorization: Bearer <your-api-key>" \ -H "Content-Type: application/json" \ -d '{ "phone_number_id": "1234567890", "access_token": "EAABwzLixnjYBO...", "verify_token": "my-webhook-verify-token" }'
Get WhatsApp Integration
GET /api/dev/v1/chatbots/{chatbot_id}/integrations/whatsapp Authorization: Bearer <api-key>GET /api/dev/v1/chatbots/{chatbot_id}/integrations/whatsapp Authorization: Bearer <api-key>
Update WhatsApp Integration
PATCH /api/dev/v1/chatbots/{chatbot_id}/integrations/whatsapp Authorization: Bearer <api-key> Content-Type: application/jsonPATCH /api/dev/v1/chatbots/{chatbot_id}/integrations/whatsapp Authorization: Bearer <api-key> Content-Type: application/json
REST API Integration
Use this integration to connect the chatbot to any custom channel via a webhook.
Create REST API Integration
POST /api/dev/v1/chatbots/{chatbot_id}/integrations/rest-api Authorization: Bearer <api-key> Content-Type: application/jsonPOST /api/dev/v1/chatbots/{chatbot_id}/integrations/rest-api Authorization: Bearer <api-key> Content-Type: application/json
Body
webhook_urlstringrequiredThe URL Conversation Engine will POST outbound messages to.
namestringoptionalFriendly name for this REST integration.
curl -X POST http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/rest-api \ -H "Authorization: Bearer <your-api-key>" \ -H "Content-Type: application/json" \ -d '{ "webhook_url": "https://yourserver.com/chatbot-webhook", "name": "My REST Integration" }'curl -X POST http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/rest-api \ -H "Authorization: Bearer <your-api-key>" \ -H "Content-Type: application/json" \ -d '{ "webhook_url": "https://yourserver.com/chatbot-webhook", "name": "My REST Integration" }'
Get / Update REST API Integration
GET /api/dev/v1/chatbots/{chatbot_id}/integrations/rest-api PATCH /api/dev/v1/chatbots/{chatbot_id}/integrations/rest-api Authorization: Bearer <api-key>GET /api/dev/v1/chatbots/{chatbot_id}/integrations/rest-api PATCH /api/dev/v1/chatbots/{chatbot_id}/integrations/rest-api Authorization: Bearer <api-key>
Web Widget Integration
Embed a real-time chat widget on any website. The widget loads asynchronously, connects over Socket.IO, and supports full conversation history, buttons, lists, and media messages.
Create Web Integration
POST /api/dev/v1/chatbots/{chatbot_id}/integrations/web Authorization: Bearer <api-key> Content-Type: application/jsonPOST /api/dev/v1/chatbots/{chatbot_id}/integrations/web Authorization: Bearer <api-key> Content-Type: application/json
Body
namestringrequiredA friendly name for this web integration.
allowed_domainsarrayoptionalList of domains allowed to load the widget (e.g. ["yoursite.com", "app.yoursite.com"]). Leave empty to allow all domains.
greeting_messagestringoptionalThe first message shown when a visitor opens the widget.
widget_configobjectoptionalAppearance and behaviour overrides - see the Widget Config reference below.
widget_config fields
bot_display_namestringoptionalName shown in the widget header and on bot messages. Defaults to "Assistant".
primary_colorstringoptionalHex color for the bubble, header, and user messages. Defaults to "#0066ff".
secondary_colorstringoptionalHex color for bot message bubbles. Defaults to "#f0f4ff".
background_colorstringoptionalHex color for the chat message area background.
bubble_textstringoptionalText shown on the floating bubble button. Defaults to "Need Help?".
bot_avatar_iconstringoptionalIcon key for the bot avatar. One of: "" (Conversation Engine default), "robot", "headset", "sparkle", "heart", "store", "graduation", "chat".
font_sizenumberoptionalBase font size in pixels (12–24). Defaults to 13.5.
positionstringoptionalWidget placement. One of "bottom-right" (default) or "bottom-left".
widthnumberoptionalChat window width in pixels (280–600). Defaults to 380.
heightnumberoptionalChat window height in pixels (400–800). Defaults to 600.
allowed_pathsarrayoptionalURL path patterns where the widget should appear. Supports exact paths ("/contact") and wildcard prefix matching ("/shop/*"). When null or empty the widget appears on every page.
curl -X POST http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/web \ -H "Authorization: Bearer <your-api-key>" \ -H "Content-Type: application/json" \ -d '{ "name": "Main Site Widget", "allowed_domains": ["yoursite.com"], "greeting_message": "Hi! How can I help you today?", "widget_config": { "bot_display_name": "Aria", "primary_color": "#0066ff", "bubble_text": "Need Help?", "position": "bottom-right", "allowed_paths": ["/contact", "/support/*"] } }'curl -X POST http://localhost:3000/api/dev/v1/chatbots/<chatbot-id>/integrations/web \ -H "Authorization: Bearer <your-api-key>" \ -H "Content-Type: application/json" \ -d '{ "name": "Main Site Widget", "allowed_domains": ["yoursite.com"], "greeting_message": "Hi! How can I help you today?", "widget_config": { "bot_display_name": "Aria", "primary_color": "#0066ff", "bubble_text": "Need Help?", "position": "bottom-right", "allowed_paths": ["/contact", "/support/*"] } }'
Response - 201 Created
{ "id": "01JUV...", "type": "web", "name": "Main Site Widget", "config": { "widget_key": "wk_AbCdEf...", "allowed_domains": ["yoursite.com"], "widget_config": { "bot_display_name": "Aria", "primary_color": "#0066ff", "position": "bottom-right", "allowed_paths": ["/contact", "/support/*"] } }, "is_active": true }{ "id": "01JUV...", "type": "web", "name": "Main Site Widget", "config": { "widget_key": "wk_AbCdEf...", "allowed_domains": ["yoursite.com"], "widget_config": { "bot_display_name": "Aria", "primary_color": "#0066ff", "position": "bottom-right", "allowed_paths": ["/contact", "/support/*"] } }, "is_active": true }
Get / Update Web Integration
GET /api/dev/v1/chatbots/{chatbot_id}/integrations/web PATCH /api/dev/v1/chatbots/{chatbot_id}/integrations/web Authorization: Bearer <api-key>GET /api/dev/v1/chatbots/{chatbot_id}/integrations/web PATCH /api/dev/v1/chatbots/{chatbot_id}/integrations/web Authorization: Bearer <api-key>
Embedding the Widget
Once your web integration is created you get a widget_key (format: wk_...). Use it to embed the widget on your site with a single <script> tag.
Script Attributes Reference
| Attribute | Required | Description |
|---|---|---|
src | yes | Widget script URL - http://localhost:3000/widget.js |
data-widget-key | yes | Your wk_... key from the integration |
data-server-url | no | Override the backend origin (useful for self-hosted deployments) |
async | recommended | Loads the script without blocking page rendering |
<!DOCTYPE html> <html lang="en"> <body> <!-- your page content --> <script src="http://localhost:3000/widget.js" data-widget-key="wk_YOUR_WIDGET_KEY" async ></script> </body> </html><!DOCTYPE html> <html lang="en"> <body> <!-- your page content --> <script src="http://localhost:3000/widget.js" data-widget-key="wk_YOUR_WIDGET_KEY" async ></script> </body> </html>
Next.js: lazyOnload strategy
lazyOnload loads the widget after all critical page resources finish. Use it in app/layout.tsx for site-wide embeds, or scope it to a nested layout for specific route groups only.
Angular SSR (Universal)
Wrap the script injection in an isPlatformBrowser(this.platformId) check to prevent it running during server-side rendering.
Shopify Online Store 2.0
On OS2.0 themes you can add a custom snippet under snippets/chat-widget.liquid and render it with {%- render 'chat-widget' -%} to keep theme.liquid tidy.
JavaScript API
The widget exposes window.ChatWidget with methods you can call from your page's JavaScript. Both methods can be called before or after the widget script loads — they take effect immediately or queue until the widget connects.
Identify the visitor
Attach a visitor identity so the same person resumes their conversation across devices, sessions, and reconnects.
<script> // Call after your login logic — pass the user's email or phone number ChatWidget.identify("alice@acme.com"); </script><script> // Call after your login logic — pass the user's email or phone number ChatWidget.identify("alice@acme.com"); </script>
Rules:
- Accepts an email or phone number
- Phone numbers are normalized to E.164 server-side (
0712...,+255712...,255 712 ...all map to the same visitor) - Invalid or synthetic emails are rejected (connection refused) — only pass real addresses
- Sanitized to
[A-Za-z0-9@._-], max 100 characters - When the same
user_idconnects again, the backend resumes their latest conversation - Omit the call entirely for anonymous visitors — behavior is unchanged
Send contact details
Pass extra visitor metadata that agents see in the conversation context panel. Useful for surfacing account info, plan tier, or any business-specific context.
<script> ChatWidget.setContactDetails({ "Name": "Alice Mongi", "Email": "alice@acme.com", "Merchant account name": "Acme Ltd", "Plan": "Pro" }); </script><script> ChatWidget.setContactDetails({ "Name": "Alice Mongi", "Email": "alice@acme.com", "Merchant account name": "Acme Ltd", "Plan": "Pro" }); </script>
Rules:
- Plain
{ key: value }object — labels are up to you - Keys are normalized to snake_case server-side (
"Merchant account name"→merchant_account_name) - Values must be scalars (string, number, boolean) — nested objects are dropped
- Max 13 fields, each value max 300 characters
- Details are merged on each reconnect: new keys are added, existing keys are updated, old keys are retained
- Stored once the visitor sends their first message
Full example
<script src="http://localhost:3000/widget.js" data-widget-key="wk_YOUR_WIDGET_KEY" async ></script> <script> // After your app authenticates the user: ChatWidget.identify("alice@acme.com"); ChatWidget.setContactDetails({ "Name": "Alice Mongi", "Merchant account name": "Acme Ltd", "Plan": "Pro" }); </script><script src="http://localhost:3000/widget.js" data-widget-key="wk_YOUR_WIDGET_KEY" async ></script> <script> // After your app authenticates the user: ChatWidget.identify("alice@acme.com"); ChatWidget.setContactDetails({ "Name": "Alice Mongi", "Merchant account name": "Acme Ltd", "Plan": "Pro" }); </script>
Independent features
identify() and setContactDetails() are fully independent. You can use one without the other, or both together. Identity drives session continuity; contact details provide extra context visible to agents.
Path-Based Visibility
By default the widget appears on every page. Use allowed_paths inside widget_config to restrict it to specific pages.
How matching works
| Pattern | Matches |
|---|---|
"/contact" | Exactly /contact |
"/shop/*" | /shop, /shop/, /shop/shoes, /shop/shoes/nike |
"/docs/*" | /docs, /docs/getting-started, /docs/api/auth |
When a visitor navigates to a page not in allowed_paths:
- The widget is hidden (
display: none) - The Socket.IO connection is disconnected - no idle connections on restricted pages
When they navigate back to an allowed page:
- The widget reappears
- The socket reconnects automatically, resuming the conversation from session history
SPA route changes
The widget listens for both popstate (browser back/forward) and intercepts history.pushState / history.replaceState to handle client-side navigation in React, Vue, Angular, and Next.js apps - no extra configuration needed.
Example - show only on support pages
{ "widget_config": { "allowed_paths": ["/support", "/support/*", "/contact", "/help/*"] } }{ "widget_config": { "allowed_paths": ["/support", "/support/*", "/contact", "/help/*"] } }
Example - show on all pages except checkout
There is no "deny list" - define the pages where the widget should appear:
{ "widget_config": { "allowed_paths": ["/", "/products/*", "/about", "/contact"] } }{ "widget_config": { "allowed_paths": ["/", "/products/*", "/about", "/contact"] } }
MCP Server Integration
MCP (Model Context Protocol) integrations give LLM agents access to tools hosted on external servers — order lookups, CRM queries, ticketing systems, internal APIs — without writing custom executor code.
Unlike channel integrations (WhatsApp, REST API, web widget), MCP integrations are many-per-chatbot: each chatbot can have multiple MCP servers registered simultaneously.
Knowledge Base or MCP?
Knowledge Base — ground answers in your own documents via RAG. Best for static content: FAQs, policies, product docs.
MCP Server — let agents call external tools and live APIs. Best for real-time actions: order lookups, CRM queries, booking systems.
You can attach both to the same chatbot. See the Knowledge Bases guide for KB setup.
For the full API reference — including all CRUD endpoints, auth types, connection testing, tool allowlists, and how to wire MCP tools into LLM_AGENT flow actions — see the dedicated guide:
Calling your own backend from a flow?
Use the API_CALL flow action to make outbound HTTP requests to your systems from within any conversation state. See the dedicated guide for full examples, variable substitution, response access, and backend setup.