Use this guide to integrate the chat bot widget in your site or application. It focuses on the public events the bot emits, the control commands it listens to, and the helper functions you can call. It intentionally avoids internal implementation details.
- The bot renders a floating chat widget that connects to your AI agent.
- It posts lifecycle events to the host page using a CustomEvent named
onBotService. - The host page can control the bot by dispatching a
botEventCustomEvent towindow.
- Overview
- Events emitted by the bot (listen on the host page)
- Events the bot listens to (control from the host page)
- Integration examples
- Data used and why (high level)
- Notes on legacy compatibility
- Include the widget via a script tag (.js link)
The widget emits browser events using window.dispatchEvent(new CustomEvent('onBotService', { detail })).
Basic listener example:
window.addEventListener('onBotService', (evt) => {
const { type, info, agentId, session, sessionKey } = evt.detail || {};
console.log('[onBotService]', type, info);
});
Common fields included in every evt.detail
- type: string — the event name (e.g., "botStarted", "openCloseChat").
- agentId: string — the agent the widget is configured to talk to.
- session: object | null — the current session object if available; null otherwise.
- sessionKey: string — the localStorage key the widget uses to persist/restore the session.
- info: object — always present and includes the universal helper functions below, plus event-specific properties for that event (for example:
{ open }foropenCloseChat,{ request }forchatSessionStarted,{ message }fornewMessageReceived,{ error, response }forchatSessionFailed).
Info helpers (always available in evt.detail.info)
-
openCloseChat(open?: boolean)
- Purpose: open or close the widget programmatically from your code.
- Behavior:
- Pass
trueto open the chat;falseto close it. - Omit the argument to toggle the current state.
- Pass
- Examples:
info.openCloseChat(true)// openinfo.openCloseChat(false)// closeinfo.openCloseChat()// toggle
-
onFinishChat()
- Purpose: end/finalize the current conversation.
- Typical effect: the conversation is closed and a
chatFinishedevent is emitted.
-
onAssessorRequest()
- Purpose: request/notify a human assessor, when that capability is enabled in your setup.
- Typical effect: sends a standardized notification/message into the conversation flow.
Quick example using the helpers and event-specific fields:
window.addEventListener('onBotService', (evt) => {
const { type, info } = evt.detail || {};
if (type === 'openCloseChat') {
const isOpen = info.open; // event-specific field
if (isOpen) info.openCloseChat(false);
}
if (type === 'chatSessionFailed') {
console.warn('Session failed:', info.error, info.response);
}
});
Common event types and payloads:
-
botStarted- When the widget has initialized and the UI is rendered.
- detail includes:
type,agentId,session,sessionKey, andinfo(helpers above).
-
openCloseChat- Fired whenever the chat is opened/closed by the user or programmatically.
- detail.info:
{ open: boolean }
-
chatSessionStarted- A chat session has been created and is ready.
- detail.info:
{ request: <payload-used-to-start-session> }
-
chat_session_started(legacy compatibility)- Emitted in some flows for older integrations.
- detail.info:
{ session: <payload> }
-
chatSessionFailed- Session creation failed.
- detail.info:
{ error: string, response: any | null }
-
newMessageReceived- A new inbound message was received (for example, an agent/bot reply).
- detail.info:
{ message: any }
-
chatFinished- The chat was finalized.
- detail.info:
{}
Dispatch a CustomEvent('botEvent') to control the widget from the host page.
// Example: programmatic login (prefill onboarding and jump to Terms)
window.dispatchEvent(new CustomEvent('botEvent', {
detail: {
type: 'login',
name: 'John Doe',
email: 'john@example.com',
phone: '3112345678', // local digits; country code is handled by the widget
metadata: { plan: 'pro', utm_source: 'ads' }
}
}));
Supported command types:
-
login- Prefills onboarding and advances to the Terms step so the user can accept and immediately start the chat.
- Accepted fields (in
detailordetail.info):name,email,phone,metadata.
-
logout- Ends the chat session.
-
sendMessage- Programmatically sends a message as if typed by the user.
- Fields:
message(string).
-
toggleChatOpen- Opens the chat widget.
-
toggleChatClose- Closes the chat widget.
Listen for lifecycle events:
window.addEventListener('onBotService', (evt) => {
const { type, info, session } = evt.detail || {};
if (type === 'chatSessionStarted') {
console.log('Session started with request:', info?.request);
}
if (type === 'newMessageReceived') {
console.log('New message:', info?.message);
}
if (type === 'chatFinished') {
console.log('Chat ended');
}
});
Programmatic open/close:
// Open
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatOpen' } }));
// Close
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatClose' } }));
Send a message programmatically:
window.dispatchEvent(new CustomEvent('botEvent', {
detail: { type: 'sendMessage', message: 'Hello from host page!' }
}));
- Name, phone, and optional email are used to identify and contact the user for the conversation.
- Agent ID routes the session to the correct agent.
- Optional metadata can include page context (e.g., UTM parameters) to help attribute and debug sessions.
- The active session is stored in localStorage under a known key (
sessionKey) so the widget can restore state after reloads.
- The widget keeps compatibility with older integrations by:
- Emitting
onBotServicewith a consistentdetailshape. - Handling
botEventcommands with bothdetail.fieldanddetail.info.fieldpatterns (older versions). - Emitting some legacy event names in specific flows (e.g.,
chat_session_started).
- Emitting
- For new integrations, prefer the event names and payloads listed above.
Use your provided script URL (referred below as SCRIPT_URL). The widget will load on any page that includes this script. You can then listen to events and control it via the same event API described above.
General pattern (works everywhere):
- Add the <script> tag that points to SCRIPT_URL.
- Optionally, wait for the onBotService "botStarted" event to confirm the widget is ready.
- To programmatically show the widget, dispatch a botEvent of type "toggleChatOpen" after it starts.
Example ready-check + open:
window.addEventListener('onBotService', (evt) => {
if (evt?.detail?.type === 'botStarted') {
// Open the widget right away (optional)
window.dispatchEvent(new CustomEvent('botEvent', {
detail: { type: 'toggleChatOpen' }
}));
}
});
If your integration requires an agent ID or other configuration, follow the instructions you received with SCRIPT_URL (for example, some deployments accept a data-agent-id attribute or read a global config object). If unsure, contact your SBX representative.
Add the script tag to your HTML (e.g., in with defer or at the end of ):
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>My Site</title>
<!-- Option A: simple include -->
<script defer src="https://example.cdn.com/path/to/SCRIPT_URL.js"></script>
<!-- Option B: include with a data attribute (only if your SCRIPT_URL supports it) -->
<!-- <script defer src="https://example.cdn.com/path/to/SCRIPT_URL.js" data-agent-id="YOUR_AGENT_ID"></script> -->
</head>
<body>
<!-- Your content -->
<script>
// Optional: open the bot when it is ready
window.addEventListener('onBotService', (evt) => {
if (evt?.detail?.type === 'botStarted') {
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatOpen' } }));
}
});
</script>
</body>
</html>
You can load the script with next/script or dynamically on the client.
- App Router (app/): place in app/layout.tsx (or a client component) using next/script
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://example.cdn.com/path/to/SCRIPT_URL.js"
strategy="afterInteractive"
/>
<Script id="sbx-bot-open" strategy="afterInteractive">
{`
window.addEventListener('onBotService', (evt) => {
if (evt?.detail?.type === 'botStarted') {
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatOpen' } }));
}
});
`}
</Script>
</body>
</html>
);
}
- Pages Router (pages/): place in pages/_app.tsx (or _document) with next/script
// pages/_app.tsx
import type { AppProps } from 'next/app';
import Script from 'next/script';
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<Script src="https://example.cdn.com/path/to/SCRIPT_URL.js" strategy="afterInteractive" />
<Script id="sbx-bot-open" strategy="afterInteractive">
{`
window.addEventListener('onBotService', (evt) => {
if (evt?.detail?.type === 'botStarted') {
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatOpen' } }));
}
});
`}
</Script>
</>
);
}
Alternative (client-only dynamic injection with useEffect):
// Any client component
import { useEffect } from 'react';
export default function BotLoader() {
useEffect(() => {
const s = document.createElement('script');
s.src = 'https://example.cdn.com/path/to/SCRIPT_URL.js';
s.async = true;
document.body.appendChild(s);
const handler = (evt) => {
if (evt?.detail?.type === 'botStarted') {
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatOpen' } }));
}
};
window.addEventListener('onBotService', handler);
return () => {
window.removeEventListener('onBotService', handler);
document.body.removeChild(s);
};
}, []);
return null;
}
Option A: add to index.html
<!-- src/index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Angular App</title>
<base href="/">
<script defer src="https://example.cdn.com/path/to/SCRIPT_URL.js"></script>
</head>
<body>
<app-root></app-root>
<script>
window.addEventListener('onBotService', (evt) => {
if (evt?.detail?.type === 'botStarted') {
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatOpen' } }));
}
});
</script>
</body>
</html>
Option B: add from a component with Renderer2
// any.component.ts
import { Component, OnInit, OnDestroy, Renderer2 } from '@angular/core';
@Component({ selector: 'app-bot-loader', template: '' })
export class BotLoaderComponent implements OnInit, OnDestroy {
private removeListener?: () => void;
constructor(private renderer: Renderer2) {}
ngOnInit(): void {
const script = this.renderer.createElement('script');
script.src = 'https://example.cdn.com/path/to/SCRIPT_URL.js';
script.defer = true;
this.renderer.appendChild(document.body, script);
const handler = (evt: any) => {
if (evt?.detail?.type === 'botStarted') {
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatOpen' } }));
}
};
window.addEventListener('onBotService', handler);
this.removeListener = () => window.removeEventListener('onBotService', handler);
}
ngOnDestroy(): void {
this.removeListener?.();
}
}
Option A: add to public/index.html (Vue CLI) or root HTML (Vite)
<!-- public/index.html or index.html -->
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Vue App</title>
<script defer src="https://example.cdn.com/path/to/SCRIPT_URL.js"></script>
</head>
<body>
<div id="app"></div>
<script>
window.addEventListener('onBotService', (evt) => {
if (evt?.detail?.type === 'botStarted') {
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatOpen' } }));
}
});
</script>
</body>
</html>
Option B: add from a component (Vue 3)
<script setup>
import { onMounted, onBeforeUnmount } from 'vue';
onMounted(() => {
const s = document.createElement('script');
s.src = 'https://example.cdn.com/path/to/SCRIPT_URL.js';
s.defer = true;
document.body.appendChild(s);
const handler = (evt) => {
if (evt?.detail?.type === 'botStarted') {
window.dispatchEvent(new CustomEvent('botEvent', { detail: { type: 'toggleChatOpen' } }));
}
};
window.addEventListener('onBotService', handler);
onBeforeUnmount(() => {
window.removeEventListener('onBotService', handler);
document.body.removeChild(s);
});
});
</script>
<template>
<div />
</template>
Notes:
- Replace https://example.cdn.com/path/to/SCRIPT_URL.js with your actual provided URL.
- If your deployment requires configuration (agent ID, language, color, etc.), apply it by the method documented with your SCRIPT_URL (e.g., data attributes on the <script> tag or a global configuration object). If not specified, the default configuration for your link will be used.
- You can always verify the widget is ready by listening for onBotService with type === 'botStarted'. After that, use botEvent commands (e.g., toggleChatOpen, sendMessage, login) as described earlier.