Web Messenger Javascript Api
This document covers the FlowXO JavaScript API for programmatic control of the chat widget.
Getting Started
Most integrations only need a few lines of code. Here are the most common patterns:
Basic Setup with User Identity
Use window.onFlowXOInit to configure the widget and identify users before it connects:
<script>
window.onFlowXOInit = async (connectionId) => {
// Fetch user data from your backend
const user = await fetchCurrentUser();
return {
member: user
? {
id: user.id,
name: user.displayName,
email: user.email,
metadata: {
plan: user.plan,
company: user.company,
},
}
: undefined,
config: {
primaryColor: '#4F46E5',
header: {
title: 'Support',
},
},
};
};
</script>
<script
src="https://messenger.flowxo.com/loader.js"
data-connection="your-connection-id"
></script>
Auto-Open After Delay
Use the flowxo:ready event to interact with the widget after it initializes:
<script>
document.addEventListener('flowxo:ready', (event) => {
const { connectionId } = event.detail;
// Open the widget after 30 seconds
setTimeout(() => {
FlowXO.open(connectionId);
}, 30000);
});
</script>
<script
src="https://messenger.flowxo.com/loader.js"
data-connection="your-connection-id"
></script>
Track Widget Events
Listen for user interactions and messages:
FlowXO.on('open', ({ connectionId }) => {
analytics.track('widget_opened', { connectionId });
});
FlowXO.on('userMessage', ({ connectionId, message }) => {
console.log('User sent:', message);
});
FlowXO.on('agentMessage', ({ connectionId, message }) => {
// Show notification if widget is closed
if (!FlowXO.isOpen(connectionId)) {
showNotification('New message from support');
}
});
Update User Identity on Login/Logout
// When user logs in
document.getElementById('login-btn').addEventListener('click', async () => {
const user = await loginUser();
FlowXO.identify({
id: user.id,
name: user.name,
email: user.email,
});
});
// When user logs out
document.getElementById('logout-btn').addEventListener('click', () => {
FlowXO.logout();
});
Global API
The FlowXO API is exposed on window.FlowXO after the loader script initializes.
FlowXO.init(options)
Programmatically initialize a new widget instance. Use this for manual control instead of auto-initialization via data-connection attributes.
const widget = await FlowXO.init({
connectionId: 'your-connection-id',
config: {
// Optional: configuration overrides
primaryColor: '#6F5FE8',
},
user: {
// Optional: user identity
id: 'user-123',
name: 'John Doe',
email: 'john@example.com',
},
});
Parameters:
| Property | Type | Required | Description |
|---|---|---|---|
connectionId |
string |
Yes | Connection ID for the widget |
config |
object |
No | Configuration overrides (merged with server config) |
user |
Member |
No | User identity to set (same as calling identify()) |
Returns: Promise<WidgetInstance | undefined>
FlowXO.identify(member) / FlowXO.identify(connectionId, member)
Identify a user for personalization and conversation continuity.
// Single widget (uses first/default instance)
FlowXO.identify({
id: 'user-123',
name: 'John Doe',
email: 'john@example.com',
avatar: 'https://example.com/john.jpg',
metadata: {
plan: 'premium',
signupDate: '2024-01-15',
},
});
// Multiple widgets (specify connection ID)
FlowXO.identify('connection-id-1', {
id: 'user-123',
name: 'John Doe',
});
Member Properties:
| Property | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique identifier for user |
name |
string |
No | Display name |
email |
string |
No | Email address |
avatar |
string |
No | Avatar image URL |
metadata |
object |
No | Custom metadata key-values |
Alias: FlowXO.setUser() - identical functionality
FlowXO.logout(connectionId?)
Clear user identity and optionally start a new conversation.
// Single widget
FlowXO.logout();
// Multiple widgets
FlowXO.logout('connection-id-1');
Alias: FlowXO.clearUser() - identical functionality
FlowXO.open(connectionId?)
Open the widget.
FlowXO.open();
FlowXO.open('connection-id-1'); // For multiple widgets
FlowXO.close(connectionId?)
Close the widget.
FlowXO.close();
FlowXO.close('connection-id-1'); // For multiple widgets
FlowXO.toggle(connectionId?)
Toggle the widget open/closed.
FlowXO.toggle();
FlowXO.toggle('connection-id-1'); // For multiple widgets
FlowXO.isOpen(connectionId?)
Check if the widget is currently open.
if (FlowXO.isOpen()) {
console.log('Widget is open');
}
Returns: boolean
FlowXO.setConfig(config, connectionId?)
Update widget configuration at runtime. Changes are merged with existing config and persisted to local storage.
FlowXO.setConfig({
primaryColor: '#FF5733',
header: {
title: 'Updated Title',
},
});
Launcher Customization
Customize the launcher button appearance at runtime:
// Use a custom image as the launcher (full mode)
FlowXO.setConfig({
launcher: {
mode: 'full',
shape: 'rounded',
iconUrl: 'https://example.com/mascot.png',
closeIconUrl: 'https://example.com/close.png',
},
});
// Custom icon with circle shape (default mode)
FlowXO.setConfig({
launcher: {
mode: 'icon',
shape: 'circle',
iconUrl: 'https://example.com/chat-icon.svg',
color: '#6F5FE8',
},
});
// Square launcher with no shadow
FlowXO.setConfig({
launcher: {
mode: 'full',
shape: 'none',
iconUrl: 'https://example.com/square-mascot.png',
},
});
Launcher Properties:
| Property | Type | Default | Description |
|---|---|---|---|
mode |
"icon" | "full" |
"icon" |
Display mode. "full" uses the image as the entire launcher |
shape |
"circle" | "rounded" | "none" |
"circle" |
Shape of the launcher. "none" = square with no shadow |
iconUrl |
string |
- | Custom icon/image URL |
closeIconUrl |
string |
- | Custom close icon URL (when widget is open) |
color |
string |
- | Background color (only applies in "icon" mode) |
Note: Configuration changes are persisted to local storage and will be applied on subsequent page loads.
FlowXO.setLocale(locale, connectionId?)
Set the widget's language/locale for UI strings and localized content.
// Set to Spanish
FlowXO.setLocale('es');
// Set to French for a specific widget
FlowXO.setLocale('fr', 'support-widget');
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
locale |
string |
Yes | Language code (e.g., "en", "es", "fr") |
connectionId |
string |
No | Target widget (defaults to first instance) |
This is a convenience method equivalent to FlowXO.setConfig({ language: locale }).
FlowXO.sendMessage(text, connectionId?)
Send a message programmatically as the user.
FlowXO.sendMessage('Hello, I need help with my order');
If the widget is closed, the message is queued and sent when opened.
FlowXO.getHistory(connectionId?)
Get the conversation history.
const messages = await FlowXO.getHistory();
console.log(messages);
// [
// { id: "1", role: "user", text: "Hello", timestamp: "2024-01-15T10:00:00Z" },
// { id: "2", role: "assistant", text: "Hi! How can I help?", timestamp: "2024-01-15T10:00:01Z" }
// ]
Returns: Promise<HistoryMessage[]>
HistoryMessage Properties:
| Property | Type | Description |
|---|---|---|
id |
string |
Message ID |
role |
"user" | "assistant" |
Message sender |
text |
string |
Message text content |
timestamp |
string |
ISO timestamp |
FlowXO.clearConversation(connectionId?)
Clear the conversation and start fresh.
FlowXO.clearConversation();
FlowXO.destroy(connectionId?)
Destroy a widget instance, removing it from the DOM.
FlowXO.destroy();
FlowXO.destroy('connection-id-1'); // For multiple widgets
Events
Subscribe to widget events to react to user interactions and widget state changes.
FlowXO.on(event, callback)
Subscribe to a widget event.
FlowXO.on('message', (data) => {
console.log('New message:', data);
});
FlowXO.off(event, callback)
Unsubscribe from a widget event.
const handler = (data) => console.log(data);
FlowXO.on('message', handler);
// Later...
FlowXO.off('message', handler);
Event Types
| Event | Description | Data |
|---|---|---|
open |
Widget was opened | { connectionId } |
close |
Widget was closed | { connectionId } |
message |
Any message (user or agent) | { connectionId, message } |
userMessage |
User sent a message | { connectionId, message } |
agentMessage |
Agent sent a message | { connectionId, message } |
error |
An error occurred | { connectionId, error } |
connectionChange |
WebSocket connection state changed | { connectionId, connected } |
conversationCleared |
Conversation was cleared | { connectionId } |
Event Example
// Track when users open/close the widget
FlowXO.on('open', ({ connectionId }) => {
analytics.track('widget_opened', { connectionId });
});
FlowXO.on('close', ({ connectionId }) => {
analytics.track('widget_closed', { connectionId });
});
// React to new messages
FlowXO.on('agentMessage', ({ connectionId, message }) => {
// Show browser notification if widget is closed
if (!FlowXO.isOpen(connectionId)) {
showNotification('New message from support');
}
});
Initialization Hook
Use window.onFlowXOInit to customize widget initialization. This callback is called for each widget instance before it connects, allowing you to set user identity and configuration dynamically.
<script>
window.onFlowXOInit = async (connectionId) => {
// Fetch user data from your backend
const user = await fetchCurrentUser();
return {
// Set user identity (optional)
member: user
? {
id: user.id,
name: user.displayName,
email: user.email,
}
: undefined,
// Override configuration (optional)
config: {
primaryColor: '#6F5FE8',
header: {
title: 'Help Center',
},
},
};
};
</script>
<script
src="https://messenger.flowxo.com/loader.js"
data-connection="your-connection-id"
></script>
Return Properties:
| Property | Type | Description |
|---|---|---|
member |
Member |
User identity to set (optional) |
config |
object |
Configuration overrides (optional) |
The callback receives connectionId as a parameter, which is useful when you have multiple widgets on the same page with different configurations.
Loaded Event
The flowxo:loaded event fires when the FlowXO API is ready but before any widgets are auto-initialized. Use this for programmatic initialization when you want full control over when widgets are created.
document.addEventListener('flowxo:loaded', (event) => {
const { FlowXO } = event.detail;
// API is ready - initialize widgets programmatically
FlowXO.init({
connectionId: 'support-bot',
user: { id: currentUser.id, name: currentUser.name },
});
});
Event Detail:
| Property | Type | Description |
|---|---|---|
FlowXO |
FlowXOAPI |
The FlowXO API object |
Use case: Load the script without data-connection to prevent auto-init, then use flowxo:loaded to initialize widgets conditionally (e.g., only for logged-in users, or after certain page conditions are met).
Ready Event
The flowxo:ready event fires when a widget instance has been initialized and is ready to use. Use this to interact with a specific widget after it's created.
document.addEventListener('flowxo:ready', (event) => {
const { connectionId, instance } = event.detail;
console.log('Widget ready:', connectionId);
// Now safe to call API methods
FlowXO.open(connectionId);
});
Event Detail:
| Property | Type | Description |
|---|---|---|
connectionId |
string |
The widget's connection ID |
instance |
WidgetInstance |
The widget instance (internal) |
For multiple widgets, the event fires once for each widget as it becomes ready.
Multiple Widget Instances
When embedding multiple widgets on a page, pass the connectionId parameter to target specific instances:
// Initialize two widgets
await FlowXO.init({ connectionId: 'sales-bot' });
await FlowXO.init({ connectionId: 'support-bot' });
// Control specific widgets
FlowXO.open('sales-bot');
FlowXO.close('support-bot');
// Identify user on specific widget
FlowXO.identify('sales-bot', {
id: 'user-123',
name: 'John',
});
When connectionId is omitted, the API operates on the first/default widget instance.
Debug Mode
Enable debug mode for development by adding ?fxo_debug=true to the page URL:
https://yoursite.com/page?fxo_debug=true
Debug mode provides:
- Detailed console logging
- Access to
FlowXO._debugutilities
// When debug mode is enabled
FlowXO._debug.log('custom', 'Debug message');
FlowXO._debug.getInstances(); // Get all widget instances
Complete Example
<!DOCTYPE html>
<html>
<head>
<title>FlowXO Widget Example</title>
</head>
<body>
<!-- Initialize callback (runs before widget connects) -->
<script>
window.onFlowXOInit = async (connectionId) => {
// Optionally fetch user from your backend
const user = await fetchCurrentUser().catch(() => null);
return {
member: user
? {
id: user.id,
name: user.name,
email: user.email,
}
: undefined,
config: {
primaryColor: '#6F5FE8',
header: {
title: 'Help Center',
subtitle: 'We typically reply within minutes',
},
launcher: {
mode: 'full',
shape: 'rounded',
iconUrl: 'https://example.com/mascot.png',
closeIconUrl: 'https://example.com/close.png',
},
},
};
};
// React to widget ready event
document.addEventListener('flowxo:ready', (event) => {
const { connectionId } = event.detail;
console.log('Widget ready:', connectionId);
// Set up event listeners
FlowXO.on('open', () => console.log('Widget opened'));
FlowXO.on('close', () => console.log('Widget closed'));
FlowXO.on('userMessage', ({ message }) =>
console.log('User said:', message)
);
});
</script>
<!-- Load the widget -->
<script
src="https://messenger.flowxo.com/loader.js"
data-connection="your-connection-id"
async
></script>
<!-- Your application code -->
<script>
// Identify user when they log in (after initial load)
document
.getElementById('login-btn')
.addEventListener('click', async () => {
const user = await loginUser();
FlowXO.identify({
id: user.id,
name: user.name,
email: user.email,
});
});
// Clear identity on logout
document.getElementById('logout-btn').addEventListener('click', () => {
FlowXO.logout();
});
// Custom button to open widget
document.getElementById('help-btn').addEventListener('click', () => {
FlowXO.open();
});
</script>
<button id="login-btn">Log In</button>
<button id="logout-btn">Log Out</button>
<button id="help-btn">Get Help</button>
</body>
</html>
Configuration Options Reference
| Property | Type | Description |
|---|---|---|
| connectionId | string | Unique identifier for this chat connection |
| name | string | Display name of the connection |
| workspaceId | string | Workspace ID this connection belongs to |
| organizationId | string | Organization ID |
| settings | WebChatSettings | Widget configuration settings (see below) |
| token | string | JWT token for authentication (generated at edge) |
WebChatSettings
| Property | Type | Description |
|---|---|---|
| Display Mode | ||
| mode | "popup" | "sidebar" | "fullscreen" | Widget display mode |
| initialState | "closed" | "open" | Initial widget state on page load |
| mobileInitialState | "closed" | "open" | Initial state on mobile devices |
| mobileMode | "fullscreen" | "floating" | "inherit" | Display mode on mobile devices |
| Theme | ||
| primaryColor | string | Primary brand color (hex) |
| accentColor | string | Accent color for buttons/links. Defaults to primaryColor |
| theme | "light" | "dark" | "auto" | Color theme |
| backgroundColor | string | Widget background color (hex) |
| backgroundOpacity | number (0-100) | Background opacity percentage |
| customCss | string | Inline custom CSS |
| customCssUrl | string | URL to external custom CSS file |
| Localization | ||
| language | string | Default language code (e.g., "en", "es") |
| languages | Record<string, Record<string, string>> | Custom translation overrides by language |
| Feature Flags | ||
| autoplayAudio | boolean | Automatically play audio messages |
| isTestConsole | boolean | Enable test/debug mode |
| Nested Configs | ||
| header | HeaderConfig | Header appearance settings |
| launcher | LauncherConfig | Launcher button settings |
| widget | WidgetConfig | Widget size and URL filtering |
| welcome | WelcomeConfig | Welcome message and suggestions |
| messages | MessagesConfig | Message bubble appearance |
| attentionGetter | AttentionGetterConfig | Attention-grabbing animation settings |
| composer | ComposerConfig | Message input settings |
| attribution | AttributionConfig | Powered-by attribution settings |
| privacy | PrivacyConfig | Privacy and data collection settings |
| newMessageSound | NewMessageSoundConfig | Sound notification settings |
| security | SecurityConfig | Domain restrictions and security |
| settingsPanel | SettingsPanelConfig | User settings panel options |
| messagePreview | MessagePreviewConfig | Message preview bubble settings |
| hostedPage | HostedPageConfig | Standalone chat page settings |
| noMessageBehavior | NoMessageBehaviorConfig | Display when no messages in conversation |
| channelId | ChannelIdConfig | Conversation isolation via storage partitioning |
HeaderConfig
| Property | Type | Description |
|---|---|---|
| title | LocalizedString | Header title text |
| subtitle | LocalizedString | Header subtitle text |
| iconUrl | string | URL to header icon/logo |
| iconBackground | string | Background color for icon (hex) |
| hidden | boolean | Hide the header entirely |
| showReset | boolean | Show clear conversation button |
LauncherConfig
| Property | Type | Description |
|---|---|---|
| disabled | boolean | Disable the launcher button entirely |
| position | "bottom-right" | "bottom-left" | Screen position |
| offset | { x?: number, y?: number } | Custom offset from default position (px) |
| color | string | Background color (hex) - only in "icon" mode |
| iconUrl | string | Custom icon URL (SVG, PNG, animated GIF) |
| closeIconUrl | string | Custom icon when widget is open |
| mode | "icon" | "full" | "icon": inset in styled button. "full": image IS the launcher |
| shape | "circle" | "rounded" | "none" | Launcher shape (only in "full" mode) |
| disableUnreadNotifications | boolean | Disable unread badge & observer |
WidgetConfig
| Property | Type | Description |
|---|---|---|
| size | "sm" | "md" | "lg" | "xl" | "full" | Widget size preset |
| urlFilter | UrlFilterConfig | URL-based widget visibility rules |
UrlFilterConfig
| Property | Type | Description |
|---|---|---|
| include | string[] | URL prefixes where widget appears (allowlist) |
| exclude | string[] | URL prefixes where widget is hidden (blocklist) |
WelcomeConfig
| Property | Type | Description |
|---|---|---|
| message | LocalizedString | Welcome message text |
| choices | LocalizedMessageChoice[] | Quick reply buttons |
| suggestions | LocalizedString[] | Suggested prompts |
LocalizedMessageChoice
| Property | Type | Description |
|---|---|---|
| value | string | Value sent when clicked (optional, defaults to label) |
| label | LocalizedString | Button display text |
| type | "quickreply" | "url" | Action type |
| urlTarget | "new_tab" | "current_tab" | Where to open URL (for type "url") |
MessagesConfig
| Property | Type | Description |
|---|---|---|
| user | MessageRoleConfig | User message appearance |
| assistant | MessageRoleConfig | AI assistant message appearance |
| liveagent | MessageRoleConfig | Human agent message appearance |
MessageRoleConfig
| Property | Type | Description |
|---|---|---|
| avatar | { enabled?: boolean, iconUrl?: string } | Avatar settings |
| bubbleColor | string | Message bubble background color (hex) |
| bubbleArrow | boolean | Show speech bubble arrow |
ComposerConfig
| Property | Type | Description |
|---|---|---|
| prompt | LocalizedString | Placeholder text in input field |
| disabled | boolean | Disable the message input |
| audio | AudioConfig | Voice message settings |
| attachments | AttachmentsConfig | File attachment settings |
AudioConfig
| Property | Type | Description |
|---|---|---|
| disabled | boolean | Disable voice messages |
| maxLengthInSeconds | number | Maximum recording length |
| autosend | boolean | Auto-send when recording stops |
AttachmentsConfig
| Property | Type | Description |
|---|---|---|
| disabled | boolean | Disable file attachments |
| maxFileSizeInBytes | number | Maximum file size |
| maxAttachments | number | Maximum number of attachments per message |
| types | string[] | Allowed MIME types |
| autosend | boolean | Auto-send after attaching |
| imageOnly | boolean | Only allow image attachments |
AttentionGetterConfig
| Property | Type | Description |
|---|---|---|
| text | LocalizedString | Attention-getter message text |
| delayInSeconds | number | Delay before showing |
| durationInSeconds | number | How long to display |
| expirationInDays | number | Days before showing again |
| intensity | "subtle" | "moderate" | "attention-grabbing" | Animation intensity |
| playSound | boolean | Play sound with animation |
| soundUrl | string | Custom sound URL |
PrivacyConfig
| Property | Type | Description |
|---|---|---|
| forgetChat | boolean | Clear conversation when widget closes |
| zeroFootprint | boolean | Prevent any localStorage usage |
| collectIp | boolean | Collect IP address and precise location (default: true) |
SecurityConfig
| Property | Type | Description |
|---|---|---|
| allowedDomains | string[] | Domains where widget can be embedded |
| allowEvaluation | boolean | Allow JavaScript expression evaluation |
Other Configs
| Config | Properties |
|---|---|
| NewMessageSoundConfig | enabled?: boolean, soundUrl?: string |
| MessagePreviewConfig | enabled?: boolean |
| HostedPageConfig | enabled?: boolean |
| AttributionConfig | disabled?: boolean, text?: LocalizedString, url?: string |
| SettingsPanelConfig | enabled?: boolean, features?: { soundToggle?: boolean, clearConversation?: boolean } |
NoMessageBehaviorConfig
| Property | Type | Description |
|---|---|---|
| type | "typing_indicator" | "loading_message" | "none" | Display type when no messages |
| loadingMessageText | LocalizedString | LocalizedString[] | Loading message text(s) |
| cycleIntervalMs | number | Interval for cycling messages (default: 3000) |
| timeoutSeconds | number | Hide after timeout |
ChannelIdConfig
| Property | Type | Description |
|---|---|---|
| prefix | string | Prefix for generated channel IDs |
| separator | string | Separator between prefix and ID (default: "_") |
LocalizedString Type
| Format | Example |
|---|---|
| Simple string | "Hello!" |
| Object with translations | { "default": "Hello!", "translations": { "es": "¡Hola!" } } |
Sample JSON
{
"connectionId": "conn_abc123xyz",
"name": "Acme Support Bot",
"workspaceId": "ws_def456uvw",
"organizationId": "org_ghi789rst",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"settings": {
"mode": "popup",
"initialState": "closed",
"mobileInitialState": "closed",
"mobileMode": "fullscreen",
"primaryColor": "#4F46E5",
"accentColor": "#7C3AED",
"theme": "light",
"backgroundColor": "#FFFFFF",
"backgroundOpacity": 100,
"customCss": ".fxo-header { font-weight: bold; }",
"customCssUrl": "https://example.com/custom-widget.css",
"language": "en",
"languages": {
"es": {
"composer.placeholder": "Escribe un mensaje..."
}
},
"autoplayAudio": false,
"isTestConsole": false,
"header": {
"title": {
"default": "Acme Support",
"translations": { "es": "Soporte Acme" }
},
"subtitle": "We typically reply within minutes",
"iconUrl": "https://example.com/logo.png",
"iconBackground": "#E0E7FF",
"hidden": false
},
"launcher": {
"disabled": false,
"position": "bottom-right",
"offset": { "x": 10, "y": 20 },
"color": "#4F46E5",
"iconUrl": "https://example.com/chat-icon.svg",
"closeIconUrl": "https://example.com/close-icon.svg",
"mode": "icon",
"shape": "circle"
},
"widget": {
"size": "md",
"urlFilter": {
"include": ["https://example.com/support", "https://example.com/help"],
"exclude": ["https://example.com/admin"]
}
},
"welcome": {
"message": "Hi there! How can I help you today?",
"choices": [
{
"label": "Track my order",
"value": "track_order",
"type": "quickreply"
},
{
"label": "Visit FAQ",
"type": "url",
"value": "https://example.com/faq",
"urlTarget": "new_tab"
}
],
"suggestions": ["What are your hours?", "How do I return an item?"]
},
"messages": {
"user": {
"avatar": { "enabled": false },
"bubbleColor": "#E0E7FF",
"bubbleArrow": true
},
"assistant": {
"avatar": { "enabled": true, "iconUrl": "https://example.com/bot-avatar.png" },
"bubbleColor": "#F3F4F6",
"bubbleArrow": true
},
"liveagent": {
"avatar": { "enabled": true, "iconUrl": "https://example.com/agent-avatar.png" },
"bubbleColor": "#FEF3C7",
"bubbleArrow": true
}
},
"composer": {
"prompt": "Type your message...",
"disabled": false,
"audio": {
"disabled": false,
"maxLengthInSeconds": 120,
"autosend": false
},
"attachments": {
"disabled": false,
"maxFileSizeInBytes": 10485760,
"maxAttachments": 5,
"types": ["image/*", "application/pdf"],
"autosend": false,
"imageOnly": false
}
},
"attentionGetter": {
"text": "Need help? We're here for you!",
"delayInSeconds": 30,
"durationInSeconds": 10,
"expirationInDays": 7,
"intensity": "moderate",
"playSound": true,
"soundUrl": "https://example.com/notification.mp3"
},
"attribution": {
"disabled": false,
"text": "Powered by Flow XO",
"url": "https://flowxo.com"
},
"privacy": {
"forgetChat": false,
"zeroFootprint": false,
"collectIp": true
},
"newMessageSound": {
"enabled": true,
"soundUrl": "https://example.com/message-sound.mp3"
},
"security": {
"allowedDomains": ["example.com", "*.example.com"],
"allowEvaluation": false
},
"settingsPanel": {
"enabled": true,
"features": {
"soundToggle": true,
"clearConversation": true
}
},
"messagePreview": {
"enabled": true
},
"hostedPage": {
"enabled": true
},
"noMessageBehavior": {
"type": "typing_indicator",
"loadingMessageText": "Connecting you with support...",
"cycleIntervalMs": 3000,
"timeoutSeconds": 30
},
"channelId": {
"prefix": "acme",
"separator": "_"
}
}
}
Next Steps
- Customizing the Web Messenger — UI-based settings (colors, avatars, features, privacy)
- Customizing with CSS — CSS variables, class selectors, and advanced styling recipes
- Using Metadata — Identify users, pass custom data, and collect visitor information
- Installation & Embedding Options — Widget, hosted page, and embed options