Resources

Integrations

Connect chatbots to WhatsApp, REST API, web widget channels, and external MCP tool servers.

Overview

Integrations connect a chatbot to an external messaging channel or tool server. Supported types:

ChannelType valueDescription
WhatsAppwhatsappWhatsApp Business API via Meta
REST APIrest-apiGeneric REST webhook for custom integrations
Web WidgetwebEmbeddable chat widget for websites
MCP ServermcpRemote tool servers for LLM agents (Model Context Protocol)

List Integrations

Returns all integrations attached to a chatbot.

http
GET /api/dev/v1/chatbots/{chatbot_id}/integrations
Authorization: Bearer <api-key>
bash
curl -X GET https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/integrations \
  -H "Authorization: Bearer <your-api-key>"

Response - 200 OK

json
{
  "items": [
    {
      "id": "01JST...",
      "chatbot_id": "01JMXYZ...",
      "type": "whatsapp",
      "is_active": true,
      "created_at": "2026-01-25T11:00:00Z"
    }
  ],
  "total": 1
}

Get Integration

http
GET /api/dev/v1/chatbots/{chatbot_id}/integrations/{integration_id}
Authorization: Bearer <api-key>
bash
curl -X GET https://developers.sarufi.io/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.

http
DELETE /api/dev/v1/chatbots/{chatbot_id}/integrations/{integration_id}
Authorization: Bearer <api-key>
bash
curl -X DELETE https://developers.sarufi.io/api/dev/v1/chatbots/<chatbot-id>/integrations/<integration-id> \
  -H "Authorization: Bearer <your-api-key>"

Response - 204 No Content


WhatsApp Integration

Create WhatsApp Integration

http
POST /api/dev/v1/chatbots/{chatbot_id}/integrations/whatsapp
Authorization: Bearer <api-key>
Content-Type: application/json

Body

phone_number_idstringrequired

Meta WhatsApp Business phone number ID.

access_tokenstringrequired

Permanent access token from Meta for Business.

verify_tokenstringrequired

Token you set when configuring the Meta webhook.

bash
curl -X POST https://developers.sarufi.io/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

http
GET /api/dev/v1/chatbots/{chatbot_id}/integrations/whatsapp
Authorization: Bearer <api-key>

Update WhatsApp Integration

http
PATCH /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

http
POST /api/dev/v1/chatbots/{chatbot_id}/integrations/rest-api
Authorization: Bearer <api-key>
Content-Type: application/json

Body

webhook_urlstringrequired

The URL Sarufi will POST outbound messages to.

namestringoptional

Friendly name for this REST integration.

bash
curl -X POST https://developers.sarufi.io/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

http
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

http
POST /api/dev/v1/chatbots/{chatbot_id}/integrations/web
Authorization: Bearer <api-key>
Content-Type: application/json

Body

namestringrequired

A friendly name for this web integration.

allowed_domainsarrayoptional

List of domains allowed to load the widget (e.g. ["yoursite.com", "app.yoursite.com"]). Leave empty to allow all domains.

greeting_messagestringoptional

The first message shown when a visitor opens the widget.

widget_configobjectoptional

Appearance and behaviour overrides - see the Widget Config reference below.

widget_config fields

bot_display_namestringoptional

Name shown in the widget header and on bot messages. Defaults to "Assistant".

primary_colorstringoptional

Hex color for the bubble, header, and user messages. Defaults to the deployment's brand colour, or "#2a6fdb" when it sets none.

secondary_colorstringoptional

Hex color for bot message bubbles. When unset, the widget tints bot bubbles itself.

background_colorstringoptional

Hex color for the chat message area background.

bubble_textstringoptional

Text shown on the floating bubble button. Defaults to "Need Help?".

bot_avatar_iconstringoptional

Icon key for the bot avatar. One of: "" (Sarufi default), "robot", "headset", "sparkle", "heart", "store", "graduation", "chat".

font_sizenumberoptional

Base font size in pixels, from 12 to 24. Defaults to 14.

positionstringoptional

Widget placement. One of "bottom-right" (default) or "bottom-left".

allowed_pathsarrayoptional

URL 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.

The widget sizes its own window to the visitor's screen, so there is no width or height to set.

The widget also reads these widget_config fields. The dashboard's web integration form sets all of them except bot_avatar_url.

FieldTypeWhat it does
brand_logo_urlstringYour logo, shown on the bubble and, when no avatar is set, in the header
bot_avatar_urlstringA photo for the header avatar
enable_suggestionsbooleanShows AI-generated follow-up questions as chips after each reply. Off by default
max_suggestionsnumberHow many chips, from 1 to 5. Defaults to 3
suggestion_topicsarrayTopics that steer the suggestions, such as ["fees", "admissions"]
offline_pushbooleanLets a visitor who has left ask for a browser notification of new replies. On by default
notification_previewbooleanWhether that notification quotes the reply. On by default; turn it off if replies can hold private details
bash
curl -X POST https://developers.sarufi.io/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

json
{
  "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

http
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

AttributeRequiredDescription
srcyesWidget script URL - https://sarufi.io/widget.js
data-widget-keyyesYour wk_... key from the integration
data-server-urlnoOverride the backend origin (useful for self-hosted deployments)
data-soundsno"false" starts the widget muted. A visitor's own mute choice is remembered and wins
data-modeno"page" fills the element named by data-container with an open chat that cannot be closed, for a page that is only the chat
data-containerwith data-modeA CSS selector for the element the chat fills, such as "#chat"
asyncrecommendedLoads the script without blocking page rendering
html
<!DOCTYPE html>
<html lang="en">
  <body>
    <!-- your page content -->
    <script
      src="https://sarufi.io/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 (and window.SarufiWidget, the same object) once its script has loaded, so call it from code that runs after that, such as a load listener. A call made before the chat connects takes effect when it does.

MethodWhat it does
identify(userId)Names an anonymous visitor so their conversation resumes. See below
identify({ token })Signs the visitor in with a host token your server mints, for tools that need a signed-in user
signOut()Drops the host token; the chat carries on as an anonymous visitor
onTokenExpired(fn)Called when the host token expires; return a fresh token, or a promise of one
setContactDetails(details)Sends details your team sees beside the conversation. See below
registerTools(handlers)Registers the functions the agent may call on your page
tool(spec)Declares a tool next to its handler, for definitions signed into the host token
tThe schema builder tool() takes: t.string(), t.integer(), t.enum([...]) and more
toolManifest()The canonical definitions and the digest a host token signs for them
setTools(names)Limits the live tools on this page; null lifts the limit
debug(on)Console diagnostics for setting up tools; never prints tokens, args or results
onToolCall(fn)Called as each tool call starts
onToolResult(fn)Called as each tool call settles

Everything about tools, tokens and their safety rules is in Website tools.

Identify the visitor

Attach a visitor identity so the same person resumes their conversation across devices, sessions, and reconnects.

html
<script>
  // Call after your login logic, with 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), so only pass real addresses
  • Control characters, angle brackets, quotes, backticks and backslashes are removed, surrounding spaces trimmed, and the result cut to 100 characters
  • When the same user_id connects again, the backend resumes their latest conversation
  • Omit the call entirely for anonymous visitors; behaviour is unchanged
  • This names a visitor, it does not verify one. For actions on a signed-in user's behalf use identify({ token })

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.

html
<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

html
<script
  src="https://sarufi.io/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

PatternMatches
"/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

json
{
  "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:

json
{
  "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:

MCP Server Integration →

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.

API_CALL Action Guide →