Botonom Window
Embed a live AI chat assistant on your website. Greet visitors, answer questions with rich interactive cards, navigate pages, and point to UI elements — all from a single floating widget.
This guide begins with Overview, continues through Quick Start, npm Package Installation, Configuration Reference, and finishes with ChatTrigger Component.
Overview
Botonom Window is a floating AI assistant widget you embed on any page. It lives in the bottom-right corner and gives your visitors instant, intelligent help without leaving the page.
What it can do
| Capability | Description |
|---|---|
| Greet visitors | A chat bubble appears after page load with a welcome message — works 24/7 |
| Answer questions | Keyword-matched responses with plans for full AI integration |
| Rich responses | Bot messages can contain interactive cards for agents, skills, and pages |
| Navigate | Redirect visitors to any page on your site via inline page markers |
| Point & highlight | Animated pointer finger and highlight rings draw attention to specific UI elements |
| Contextual triggers | Drop-in ChatTrigger buttons let you open the chat with pre-loaded context from anywhere |
| Dark mode | Full theme support — automatically adapts to your site's color scheme |
| Mobile-first | Responsive design: compact on desktop, full-screen overlay on mobile |
Quick Start
1. Add the Widget
Drop the ChatWidget component anywhere in your layout — it renders as a fixed-position floating button. All you need is your agent's UUID:
import { ChatWidget } from "@botonom/chat-widget";
function App() {
return (
<div>
<YourContent />
<ChatWidget agentId="d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90" />
</div>
);
}
The widget fetches your agent's name, avatar, brand colors, and conversation config from the Botonom API automatically. No manual branding setup needed.
The widget also creates a ChatEventBus singleton and exposes it at window.__botonom_chat.
2. Talk to It
Open your browser console and try:
__botonom_chat.emit("open")
__botonom_chat.emit("botMessage", { text: "Hello there!" })
3. Add Context Triggers (optional)
Place ChatTrigger buttons next to features that might need explanation:
import { ChatTrigger } from "@botonom/chat-widget";
<ChatTrigger
variant="link"
label="Have questions? Ask Judith"
systemPrompt="User is on the pricing page"
botMessage="I can help you pick the right plan!"
/>
That's it — the widget handles opening, context injection, and the bot response automatically.
npm Package Installation
Botonom Window is published as a standalone npm package. The widget is fully self-contained — you provide a single agent ID and the widget handles everything else: fetching the agent profile, loading the animated avatar, applying brand colors, and connecting to the conversation API.
Install
npm install @botonom/chat-widget
# peer deps
npm install react lucide-react
Minimal Setup
import { ChatWidget } from "@botonom/chat-widget";
function App() {
return (
<div>
<YourContent />
<ChatWidget agentId="your-agent-uuid" />
</div>
);
}
That's it — one prop, one line. The widget calls the Botonom API with the agent UUID and automatically resolves:
| Auto-resolved | Source |
|---|---|
| Agent name & role | Agent profile from API |
| Brand colors & gradient | Workspace branding settings |
| Animated avatar SVG | Agent's avatar asset (animated, expression-aware) |
| Conversation context | Agent's trained knowledge base |
| Localization strings | Workspace language configuration |
How It Works Under the Hood
┌──────────────────┐ GET /v1/agents/{uuid}/widget-config
│ ChatWidget │ ────────────────────────────────────────▶ Botonom API
│ agentId="abc.." │ ◀────────────────────────────────────────
└──────────────────┘ { name, role, colors, avatarUrl,
locale, responses, ... }
On mount, the widget sends a single API request with the provided UUID. The response contains the complete agent profile — name, role, brand colors, animated avatar SVG URL, and conversation configuration. The widget caches this data for the session so subsequent opens are instant.
Where to Find Your Agent ID
- Go to panel.botonom.com → Agents
- Click on the agent you want to embed
- Open Settings → Widget Embed
- Copy the Agent ID (UUID format:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
Or fetch it via the API:
curl -H "Authorization: Bearer $API_KEY" \
https://api.botonom.com/v1/agents | jq '.[0].id'
What's Included in the Package
| Module | Purpose |
|---|---|
ChatWidget | Main floating widget (FAB + chat panel + streaming) |
ChatTrigger | Drop-in trigger button with 4 variants |
ChatPointerOverlay | Pointer finger + highlight ring overlay |
RichMessageRenderer | Inline marker parser — interactive cards |
ChatEventBus | Framework-agnostic event bus singleton |
useChatWidget | React hook for type-safe bus access |
useOnChatEvent | React hook for subscribing to outbound events |
Package Boundary
The package has exactly two peer dependencies:
| Dependency | Why |
|---|---|
react (>=18) | Hooks, JSX |
lucide-react | Icons (Send, X, HelpCircle, etc.) |
No Tailwind, no React Router, no i18n library, no build-tool plugins. Tailwind classes used inside the widget are optional — the widget ships inline styles as fallback.
Configuration Reference
The only required prop is agentId. Everything else is auto-resolved from the Botonom API, but you can override any field locally if needed.
Required
| Prop | Type | Description |
|---|---|---|
agentId | string | The UUID of your Botonom agent. The widget fetches all configuration from the API using this ID. |
<ChatWidget agentId="d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90" />
What the API Returns
When the widget mounts, it fetches the agent's widget configuration:
GET https://api.botonom.com/v1/agents/{agentId}/widget-config
{
"agent": {
"name": "Judith",
"role": "Brand Representative",
"avatarUrl": "https://cdn.botonom.com/avatars/judith-animated.svg",
"avatarExpressions": ["idle", "thinking", "talking"]
},
"branding": {
"companyName": "Botonom",
"primaryColor": "#3B82F6",
"gradientFrom": "#2563EB",
"gradientTo": "#3B82F6"
},
"widget": {
"showDelay": 2000,
"teaserDelay": 5000,
"idleNudgeDelay": 30000,
"teaserHighlightWord": "Judith",
"locale": "en"
}
}
All of these values populate the widget automatically. The animated avatar SVG supports real-time expression states — the same technology powering the dashboard avatars.
Optional Overrides
You can still override any auto-resolved value via the config prop. Local overrides take precedence over API values:
<ChatWidget
agentId="d4e5f6a7-..."
config={{
showDelay: 0, // show FAB immediately
teaserDelay: 3000, // show teaser faster
idleNudgeDelay: 0, // disable idle nudge
}}
/>
Timing Overrides
| Field | Type | Default (API) | Description |
|---|---|---|---|
showDelay | number | 2000 | Milliseconds before the FAB appears after mount |
teaserDelay | number | 5000 | Milliseconds before the teaser bubble slides in |
idleNudgeDelay | number | 30000 | Milliseconds of inactivity before a nudge (0 = disabled) |
Locale Override
| Field | Type | Default | Description |
|---|---|---|---|
detectLocale | () => string | URL-based (/en/, /tr/) | Custom locale detection — overrides API locale |
<ChatWidget
agentId="d4e5f6a7-..."
config={{
detectLocale: () => navigator.language.slice(0, 2),
}}
/>
Loading & Error States
The widget handles all loading and error states internally:
| State | Behavior |
|---|---|
| Loading | FAB shows a subtle pulse animation while fetching agent config |
| Success | Agent avatar, name, and colors render instantly from cache |
| Network error | Widget retries 3x with exponential backoff, then falls back to generic defaults |
| Invalid UUID | Console warning + generic fallback (widget still functional) |
403 Forbidden response.ChatTrigger Component
ChatTrigger is a drop-in component you place anywhere in your UI. When clicked, it:
- Injects a system prompt (hidden AI context the user never sees)
- Opens the chat window
- Bot delivers a contextual message
Variants
| Variant | Appearance | Best for |
|---|---|---|
icon | Small circled ? icon | Inline help next to labels, headings, or form fields |
link | Text link with help icon | Below search bars, in hero sections, inside paragraphs |
button | Styled pill button | CTA areas, pricing sections, feature cards |
custom | Your own children as trigger | Full creative control — wraps any element |
Props Reference
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "icon" | "link" | "button" | "custom" | "icon" | Visual style |
label | string | "Learn more" | Text for link/button variants |
systemPrompt | string | — | Hidden context for the AI (user never sees this) |
botMessage | string | — | Bot's opening message when triggered |
userMessage | string | — | Simulates user typing a message instead |
size | "sm" | "md" | "lg" | "md" | Size of icon variant |
color | "blue" | "gray" | "white" | "inherit" | "blue" | Color theme |
highlightFab | boolean | false | Pulse the FAB before opening |
delay | number | 150 | Delay (ms) before bot speaks — lets open animation finish |
tooltip | string | — | Tooltip on hover |
Examples
{/* Icon — next to a heading */}
<h2>
Token Usage <ChatTrigger variant="icon" size="sm"
botMessage="Tokens are consumed per action..." />
</h2>
{/* Link — below a search bar */}
<ChatTrigger
variant="link"
label="Can't find what you need? Ask Judith"
color="blue"
systemPrompt="User is on the Knowledge Base."
botMessage="I can help you navigate our docs!"
/>
{/* Button — in a pricing CTA area */}
<ChatTrigger
variant="button"
label="Not sure which plan?"
color="blue"
botMessage="Let me help you pick the right plan!"
/>
{/* Custom — wrap anything */}
<ChatTrigger variant="custom" botMessage="Need help?">
<span className="underline cursor-pointer">Ask Judith</span>
</ChatTrigger>
systemPrompt to give the AI page-specific context without the user seeing it. For example: "User is viewing the Executive Secretary agent profile — salary $5/mo, skills: email, calendar, CRM".Rich Message Markers
Bot responses can contain inline markers that render as interactive mini-cards instead of plain text. This is powered by the RichMessageRenderer.
Marker Syntax
Markers follow the format [Type:value] or [Type:label|value] or [Type:label|value|extra]:
| Marker | Example | Renders As |
|---|---|---|
[Skill:id] | [Skill:Reservation|reservation] | Amber mini-card with contextual icon (calendar, mail, etc.) |
[Agent:slug] | [Agent:Secretary|secretary] | Blue mini-card with bot icon |
[Page:href] | [Page:Browse Agents|/agents] | Indigo mini-card with file icon |
[Link:label|href] | [Link:Read More|/docs] | Inline purple link |
[Action:label|action|target] | [Action:Show Me|pointer|#pricing] | Pink gradient action button |
How It Works
When the bot sends a message like:
Check out our [Agent:Executive Secretary|secretary] — it handles
email, calendar, and CRM. You can also browse the
[Skill:Reservation|reservation] skill or visit
[Page:Pricing|/pricing] for plans.
The renderer parses each [Type:...] block and replaces it with a styled, clickable mini-card:
- Skill cards — navigate to
/skills/{id} - Agent cards — navigate to
/agents/{slug} - Page cards — navigate to the given
href - Link markers — inline link to any URL
- Action buttons — trigger a UI action (pointer animation, highlight, scroll)
Skill Icon Mapping
Skill cards automatically pick a contextual icon based on the skill's name:
| Keyword in ID | Icon |
|---|---|
reservation, calendar | Calendar |
email, mail | |
order, shopping | Shopping Cart |
call, phone | Phone |
analytics, report | Bar Chart |
web | Globe |
automation | Zap |
| (fallback) | Puzzle |
[Type:...] patterns, the renderer returns plain text with zero overhead.Pointer & Highlight Overlay
The ChatPointerOverlay component adds visual effects that the bot can trigger to draw attention to specific elements on the page.
Supported Actions
| Action | What It Does |
|---|---|
pointer | An animated hand/finger appears at screen center, glides to the target element, performs a "click" press with a ripple, then fades out |
highlight | A pulsing blue ring appears around the target element for ~2.5 seconds, then fades |
scroll | Smooth-scrolls the page until the target element is centered in the viewport |
How to Target Elements
The overlay resolves targets in this order:
- CSS selector — e.g.
#pricing-table,.cta-button,[data-feature="cortex"] - data-chat-target attribute — e.g.
data-chat-target="pricing"is matched by target"pricing"
Add data-chat-target attributes to key elements you want the bot to reference:
<section data-chat-target="pricing">
<h2>Pricing</h2>
...
</section>
<button data-chat-target="hire-btn">Hire Now</button>
Triggering from Bot Messages
Use the [Action:...] marker in a bot response:
Here's our pricing section — [Action:Show Me|pointer|#pricing-table]
When the user clicks the pink "Show Me" button, the pointer finger animates toward #pricing-table.
Triggering Programmatically
// Via event bus
__botonom_chat.emit("uiAction", {
action: "pointer",
target: "#pricing-table"
})
// Highlight an element
__botonom_chat.emit("uiAction", {
action: "highlight",
target: "[data-chat-target=hire-btn]"
})
// Just scroll
__botonom_chat.emit("uiAction", {
action: "scroll",
target: "#faq-section"
})
Animation Timeline (pointer action)
| Phase | Duration | Visual |
|---|---|---|
| Appear | 500 ms | Hand scales up from center with bounce |
| Move | 600 ms | Glides to target with ease-out cubic |
| Click | 300 ms | Press down + rotate animation |
| Ripple | 500 ms | Blue ripple expands from fingertip |
| Fade out | 400 ms | Shrinks and disappears |
Event Bus Reference
Botonom Window uses a bidirectional event bus — your site sends commands into the widget, and the widget fires events out to your site.
Architecture
┌──────────────┐ ┌──────────────┐
│ Your Site │ ── inbound ──▶ │ Widget │
│ (React / │ │ (ChatWidget) │
│ vanilla) │ ◀── outbound ── │ │
└──────────────┘ └──────────────┘
Inbound Events (Site → Widget)
Control the widget from anywhere in your code:
| Event | Payload | Description |
|---|---|---|
open | — | Open the chat window |
close | — | Close the chat window |
toggle | — | Toggle open/close |
send | { text, openFirst? } | Send a visible user message — bot will respond |
botMessage | { text, openFirst? } | Bot speaks a prepared message (appears as bot bubble) |
system | { prompt } | Inject invisible AI context (user never sees this) |
clear | — | Clear all chat history |
setContext | { page?, agent?, topic?, ... } | Set metadata that influences AI responses |
setLocale | { locale: "en" | "tr" } | Switch the widget's language |
highlight | { duration? } | Pulse/glow the floating action button |
badge | { count } | Show a red notification badge (null to remove) |
nudge | { text, duration? } | Show a tooltip bubble near the FAB |
Outbound Events (Widget → Site)
Listen to these to react when the widget does something:
| Event | Payload | Description |
|---|---|---|
navigate | { href } | Bot wants to redirect the user to a URL |
uiAction | { action, target } | Bot requests a visual UI action (pointer, highlight, scroll) |
widgetOpened | — | Chat window was opened |
widgetClosed | — | Chat window was closed |
userMessage | { text } | User sent a message (useful for analytics) |
botResponse | { raw, markers[] } | Bot delivered a message with parsed marker data |
navigate event is automatically handled by the Layout component using React Router. Internal links are prefixed with the current locale (/en/... or /tr/...), and external URLs open in a new tab.React Hook — useChatWidget
For React components, use the useChatWidget hook instead of accessing the event bus directly. It provides type-safe helper methods:
import { useChatWidget } from "@botonom/chat-widget";
function MyComponent() {
const chat = useChatWidget();
return (
<div>
<button onClick={() => chat.open()}>Open Chat</button>
<button onClick={() => chat.send("What is CORTEX?")}>
Ask about CORTEX
</button>
<button onClick={() => chat.botMessage("Let me explain...")}>
Bot Speaks
</button>
<button onClick={() => chat.nudge("Need help?", 5000)}>
Show Nudge
</button>
</div>
);
}
Available Methods
| Method | Equivalent Event | Description |
|---|---|---|
chat.open() | emit("open") | Open window |
chat.close() | emit("close") | Close window |
chat.toggle() | emit("toggle") | Toggle open/close |
chat.send(text) | emit("send", {text}) | Send user message |
chat.botMessage(text) | emit("botMessage", {text}) | Bot speaks |
chat.system(prompt) | emit("system", {prompt}) | Inject hidden context |
chat.clear() | emit("clear") | Clear history |
chat.highlight(ms?) | emit("highlight", {duration}) | Pulse the FAB |
chat.badge(n) | emit("badge", {count}) | Show badge |
chat.nudge(text, ms?) | emit("nudge", {text, duration}) | Show tooltip |
chat.setContext({...}) | emit("setContext", {...}) | Set AI context |
chat.setLocale(loc) | emit("setLocale", {locale}) | Switch locale |
chat.bus | — | Direct bus access for advanced usage |
Listening to Outbound Events
Use the useOnChatEvent hook to subscribe to widget events. It auto-cleans up on unmount:
import { useOnChatEvent } from "@botonom/chat-widget";
function Analytics() {
useOnChatEvent("userMessage", ({ text }) => {
trackEvent("chat_message", { text });
});
useOnChatEvent("navigate", ({ href }) => {
console.log("Bot navigated to:", href);
});
useOnChatEvent("botResponse", ({ raw, markers }) => {
console.log("Bot said:", raw);
console.log("Markers:", markers);
});
return null;
}
Browser Console Commands
The event bus is exposed globally as window.__botonom_chat. Open DevTools (F12) and try these commands:
// ── Basic Controls ──
__botonom_chat.emit("open")
__botonom_chat.emit("close")
__botonom_chat.emit("toggle")
// ── Make the Bot Speak ──
__botonom_chat.emit("botMessage", {
text: "Hello! How can I help you today?"
})
// ── Send as User ──
__botonom_chat.emit("send", { text: "What agents do you have?" })
// ── Inject Hidden Context ──
__botonom_chat.emit("system", {
prompt: "User is a developer integrating the API"
})
// ── Rich Markers in Bot Message ──
__botonom_chat.emit("botMessage", {
text: "Try our [Agent:Executive Secretary|secretary] or browse [Page:All Agents|/agents]"
})
// ── Attention Effects ──
__botonom_chat.emit("highlight", { duration: 3000 })
__botonom_chat.emit("badge", { count: 3 })
__botonom_chat.emit("nudge", {
text: "We have a special offer!", duration: 5000
})
// ── Pointer Animation ──
__botonom_chat.emit("uiAction", {
action: "pointer", target: "#some-element"
})
// ── Highlight Ring ──
__botonom_chat.emit("uiAction", {
action: "highlight", target: ".cta-button"
})
// ── Scroll to Element ──
__botonom_chat.emit("uiAction", {
action: "scroll", target: "#faq"
})
// ── Debug Mode (logs all events) ──
__botonom_chat.debug(true)
// ── Check Listener Count ──
console.log(__botonom_chat.listenerCount)
__botonom_chat.debug(true)Dark Mode & Theming
Botonom Window automatically detects your site's theme and adapts its colors:
| Element | Light Mode | Dark Mode |
|---|---|---|
| Window background | White | Slate-900 |
| Bot bubbles | Light blue | Dark blue-900 |
| User bubbles | Blue-600 | Blue-500 |
| Input field | White + gray border | Dark slate + subtle border |
| Rich marker cards | Colored tint (50%) | Darkened colored tint |
| FAB button | Blue gradient | Blue gradient (unchanged) |
ChatTrigger Theming
The ChatTrigger component also supports dark mode through its color prop:
| Color | Light | Dark |
|---|---|---|
blue | Blue-50 bg, Blue-600 text | Blue-900/20 bg, Blue-400 text |
gray | Gray-100 bg, Gray-500 text | Gray-800 bg, Gray-400 text |
white | White/80 bg, Gray-500 text | Gray-800/80 bg, Gray-400 text |
inherit | No styles — inherits from parent | Same |
dark class on a parent element (Tailwind convention). If your site uses ThemeProvider, Botonom Window picks it up automatically.