Events System
The Nexus Chat Widget emits events for all user interactions and widget lifecycle changes. Use these events to build analytics, custom UI, integrations, and advanced features.
Available Events
The widget supports 22+ event types covering UI lifecycle, sessions, messages, interactions, voice/TTS, and errors.
UI Lifecycle Events
| Event | Description | Data |
|---|---|---|
open | Widget opened by user | { sessionId, timestamp } |
close | Widget closed by user | { sessionId, timestamp } |
minimize | Widget minimized | { previousMode, sessionId } |
maximize | Widget maximized | { previousMode, sessionId } |
Session Events
| Event | Description | Data |
|---|---|---|
sessionStart | New chat session created | { sessionId, metadata } |
sessionEnd | Chat session ended | { sessionId, duration, messageCount } |
sessionClear | User explicitly cleared chat | { sessionId?, messageCount, timestamp } |
Message Events
| Event | Description | Data |
|---|---|---|
messageStart | User submitted a message | { message, timestamp } |
messageComplete | AI response completed | { answer, queryId, duration } |
messageChunk | Streaming message chunk received | { chunk, queryId } |
Interaction Events
| Event | Description | Data |
|---|---|---|
typing | User is typing | { text, length } |
typingStop | User stopped typing | { finalText } |
feedback | User submitted feedback | { rating, messageId, comment? } |
copy | User copied message content | { content, messageId } |
customAction | Custom action button clicked | { eventName, eventData, actionId, timestamp } |
handover | Agent handover triggered | { sessionId, reason, priority, metadata?, timestamp } |
disclaimerAccept | User accepted the disclaimer | { experienceId, timestamp } |
linkClick | Link clicked in message | { href, text, protocol, params, sessionId } |
Voice & TTS Events
| Event | Description | Data |
|---|---|---|
voiceStart | Voice recognition started | { language, sessionId } |
voiceEnd | Voice recognition ended | { transcript, duration } |
ttsStart | Text-to-speech started | { text, messageId } |
ttsEnd | Text-to-speech ended | { duration, messageId } |
Error Events
| Event | Description | Data |
|---|---|---|
error | Error occurred | { type, message, stack?, details? } |
Listening to Events
Use the widget.on() method to register event listeners:
// Initialize widget
const widget = window.NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2",
});
// Listen to specific event
widget.on("messageComplete", (data) => {
console.log("AI responded:", data.answer);
console.log("Response time:", data.duration, "ms");
});
Complete Example
Here’s a comprehensive example that listens to all event types:
const widget = window.NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://nexus-api.uat.knowbl.com/api/v2",
});
// UI Lifecycle Events
widget.on("open", (data) => {
console.log("Widget opened", data.sessionId);
});
widget.on("close", (data) => {
console.log("Widget closed", data.sessionId);
});
widget.on("minimize", (data) => {
console.log("Widget minimized from", data.previousMode);
});
widget.on("maximize", (data) => {
console.log("Widget maximized from", data.previousMode);
});
// Session Events
widget.on("sessionStart", (data) => {
console.log("Session started:", data.sessionId);
});
widget.on("sessionClear", (data) => {
console.log("Session cleared:", {
sessionId: data.sessionId,
messageCount: data.messageCount,
});
});
widget.on("sessionEnd", (data) => {
console.log("Session ended:", {
sessionId: data.sessionId,
duration: data.duration,
messageCount: data.messageCount,
});
});
// Message Events
widget.on("messageStart", (data) => {
console.log("User message:", data.message);
});
widget.on("messageComplete", (data) => {
console.log("AI response:", {
answer: data.answer,
queryId: data.queryId,
duration: data.duration,
});
});
// Interaction Events
widget.on("typing", (data) => {
console.log("User typing...", data.length, "characters");
});
widget.on("typingStop", () => {
console.log("User stopped typing");
});
widget.on("feedback", (data) => {
console.log("User feedback:", data.rating, "for message", data.messageId);
if (data.comment) {
console.log("Comment:", data.comment);
}
});
widget.on("copy", (data) => {
console.log("User copied message:", data.messageId);
});
widget.on("customAction", (data) => {
console.log("Custom action triggered:", {
eventName: data.eventName,
actionId: data.actionId,
eventData: data.eventData,
});
// Handle specific custom actions
if (data.eventName === "show_animation") {
// Trigger your custom animation
console.log("Showing animation...");
}
});
widget.on("handover", (data) => {
console.log("Agent handover requested:", {
sessionId: data.sessionId,
reason: data.reason,
priority: data.priority,
metadata: data.metadata,
});
// Trigger your handover process (e.g., open live chat, notify agents)
if (data.priority === "high") {
console.log("High priority handover - notifying agents immediately");
}
});
widget.on("linkClick", (data) => {
console.log("Link clicked:", {
href: data.href,
text: data.text,
protocol: data.protocol,
params: data.params,
});
// Handle custom protocol links (e.g., tour:, action:)
if (data.protocol === "tour:") {
const tourId = data.params.id || data.params.value;
console.log("Guided tour requested:", tourId);
}
});
// Voice Events
widget.on("voiceStart", (data) => {
console.log("Voice input started, language:", data.language);
});
widget.on("voiceEnd", (data) => {
console.log("Voice input ended:", {
transcript: data.transcript,
duration: data.duration,
});
});
// TTS Events
widget.on("ttsStart", (data) => {
console.log("TTS started for message:", data.messageId);
});
widget.on("ttsEnd", (data) => {
console.log("TTS ended, duration:", data.duration);
});
// Error Handling
widget.on("error", (error) => {
console.error("Widget error:", {
type: error.type,
message: error.message,
details: error.details,
});
});
Analytics Integration
Google Analytics
Track key metrics with Google Analytics:
widget.on("sessionStart", (data) => {
gtag("event", "chat_session_start", {
session_id: data.sessionId,
});
});
widget.on("messageComplete", (data) => {
gtag("event", "chat_message_complete", {
session_id: data.sessionId,
response_time: data.duration,
answer_length: data.answer.length,
});
});
widget.on("feedback", (data) => {
gtag("event", "chat_feedback", {
session_id: data.sessionId,
rating: data.rating,
message_id: data.messageId,
});
});
widget.on("error", (error) => {
gtag("event", "chat_error", {
error_type: error.type,
error_message: error.message,
});
});Mixpanel
Track events with Mixpanel:
widget.on("sessionStart", (data) => {
mixpanel.track("Chat Session Started", {
session_id: data.sessionId,
timestamp: data.timestamp,
});
});
widget.on("messageComplete", (data) => {
mixpanel.track("Chat Message Completed", {
session_id: data.sessionId,
response_time: data.duration,
query_id: data.queryId,
});
});
widget.on("feedback", (data) => {
mixpanel.track("Chat Feedback", {
rating: data.rating,
message_id: data.messageId,
has_comment: !!data.comment,
});
});Segment
Send events to Segment:
widget.on("sessionStart", (data) => {
analytics.track("Chat Session Started", {
sessionId: data.sessionId,
});
});
widget.on("messageComplete", (data) => {
analytics.track("Chat Message Completed", {
sessionId: data.sessionId,
responseTime: data.duration,
queryId: data.queryId,
});
});Use Cases
Custom UI Updates
Update your UI based on widget state:
const statusIndicator = document.getElementById("chat-status");
widget.on("open", () => {
statusIndicator.textContent = "Chat Open";
statusIndicator.className = "status-active";
});
widget.on("close", () => {
statusIndicator.textContent = "Chat Closed";
statusIndicator.className = "status-inactive";
});
widget.on("messageStart", () => {
statusIndicator.textContent = "Processing...";
});
widget.on("messageComplete", () => {
statusIndicator.textContent = "Ready";
});Session Recording
Record chat sessions for playback:
const sessionData = {
events: [],
startTime: null,
endTime: null,
};
widget.on("sessionStart", (data) => {
sessionData.startTime = data.timestamp;
sessionData.events = [];
});
widget.on("messageStart", (data) => {
sessionData.events.push({
type: "userMessage",
content: data.message,
timestamp: data.timestamp,
});
});
widget.on("messageComplete", (data) => {
sessionData.events.push({
type: "assistantMessage",
content: data.answer,
timestamp: new Date(),
duration: data.duration,
});
});
widget.on("sessionEnd", (data) => {
sessionData.endTime = data.timestamp;
// Send to your backend for storage
fetch("/api/chat-sessions", {
method: "POST",
body: JSON.stringify(sessionData),
});
});Agent Handover Integration
Integrate with live chat systems when users request agent assistance:
const widget = window.NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://api.example.com",
messages: {
welcomeMessage: "👋 Hello! Need help?",
welcomeMessageDisplay: "bubble",
welcomeActions: {
actions: [
{
id: "talk-to-agent",
label: "Talk to Agent",
action: {
type: "trigger_handover",
payload: {
reason: "user_requested",
priority: "normal",
metadata: { source: "welcome_message" },
},
},
},
],
},
},
});
// Listen for handover requests
widget.on("handover", async (data) => {
console.log("Handover requested:", data);
// Show loading state
showHandoverNotification("Connecting you to an agent...");
try {
// Integrate with your live chat system
// Example: Intercom, Zendesk, custom live chat
const agent = await connectToLiveChat({
sessionId: data.sessionId,
reason: data.reason,
priority: data.priority,
metadata: data.metadata,
});
// Update UI
showHandoverNotification(`Connected to ${agent.name}`);
// Optional: Hide or disable the widget
if (data.priority === "high") {
widget.close();
openLiveChatWindow(agent);
}
} catch (error) {
console.error("Handover failed:", error);
showHandoverNotification("Unable to connect. Please try again.");
}
});
async function connectToLiveChat(handoverData) {
// Your integration logic here
// Example: Create Intercom conversation
// Example: Open Zendesk chat
// Example: Connect to custom live chat API
const response = await fetch("https://your-api.com/handover", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(handoverData),
});
return response.json();
}
function showHandoverNotification(message) {
// Show notification to user
console.log(message);
}
function openLiveChatWindow(agent) {
// Open your live chat interface
console.log("Opening live chat with:", agent);
}Handover Event Properties:
sessionId- Current chat session identifierreason- Why handover was requested (e.g.,"user_requested","unable_to_help","escalation")priority- Urgency level:"low","normal", or"high"metadata- Optional additional context datatimestamp- ISO timestamp when handover was triggered
Common Use Cases:
- Connect to live chat systems (Intercom, Zendesk, custom)
- Create support tickets with conversation context
- Notify available agents via Slack/email
- Route to specialized support teams based on priority
- Track handover metrics and response times
Link Click Integration
Handle clicks on links in AI responses, including custom protocol links for integrations:
// Handle link clicks in AI responses
widget.on("linkClick", (data) => {
console.log("Link clicked:", {
href: data.href,
text: data.text,
protocol: data.protocol,
params: data.params,
sessionId: data.sessionId,
});
// Handle guided tour links
if (data.protocol === "tour:") {
const tourId = data.params.id || data.params.value;
startGuidedTour(tourId);
return;
}
// Handle custom action links
if (data.protocol === "action:") {
const actionType = data.params.type || data.params.value;
handleCustomAction(actionType, data.params);
return;
}
// Handle product links
if (data.protocol === "product:") {
const productId = data.params.id || data.params.value;
openProductModal(productId);
return;
}
// Track standard link clicks for analytics
if (data.protocol === "https:" || data.protocol === "http:") {
trackLinkClick(data.href, data.text);
}
});
function handleCustomAction(actionType, params) {
switch (actionType) {
case "schedule":
openSchedulingModal(params);
break;
case "download":
initiateDownload(params.fileId);
break;
case "feedback":
openFeedbackForm(params.topic);
break;
default:
console.log("Unknown action:", actionType);
}
}
function trackLinkClick(href, text) {
// Send to analytics
if (typeof gtag !== "undefined") {
gtag("event", "link_click", {
link_url: href,
link_text: text,
event_category: "chat_interaction",
});
}
}linkClick Event Properties:
href- Full href attribute of the clicked linktext- Text content of the linkprotocol- Protocol/scheme of the link (e.g.,"tour:","https:","action:")params- Parsed parameters from the href (e.g.,{ id: "123456" })sessionId- Current chat session identifiertimestamp- ISO timestamp when link was clicked
Custom Protocol Support:
AI responses can include links with custom protocols that trigger integrations:
tour:id=123456- Launch guided tours or walkthroughsaction:type=schedule&topic=demo- Trigger custom actionsproduct:id=ABC123- Open product modals
Common Use Cases:
- Launch guided tours or interactive walkthroughs
- Trigger custom UI actions (modals, downloads, forms)
- Track outbound link clicks for analytics
- Open product details or documentation
- Execute custom business logic
Alternative: Custom Protocol Handlers
For simple protocol-to-action mapping, you can use customProtocolHandlers in the widget config instead of event listeners:
const widget = window.NexusChatWidget.init({
experienceId: "your-experience-id",
apiUrl: "https://api.example.com",
advanced: {
customProtocolHandlers: {
"walkme:": (params) => {
if (params.flowId && window.WalkMeAPI) {
window.WalkMeAPI.startFlowById(parseInt(params.flowId));
}
},
"tour:": (params) => startGuidedTour(params.id || params.value),
"product:": (params) => openProductModal(params.id)
}
}
});See Custom Protocol Handlers for full documentation.
Performance Monitoring
Track widget performance metrics:
const metrics = {
totalMessages: 0,
totalResponseTime: 0,
errors: 0,
};
widget.on("messageComplete", (data) => {
metrics.totalMessages++;
metrics.totalResponseTime += data.duration;
const avgResponseTime = metrics.totalResponseTime / metrics.totalMessages;
console.log("Average response time:", avgResponseTime, "ms");
});
widget.on("error", () => {
metrics.errors++;
console.log(
"Error rate:",
(metrics.errors / metrics.totalMessages) * 100,
"%",
);
});Tracking Session Clears
Track when users explicitly clear conversations:
// Track when users explicitly clear conversations
widget.on("sessionClear", (data) => {
// Log to analytics
analytics.track("Chat Cleared", {
sessionId: data.sessionId,
messageCount: data.messageCount,
timestamp: data.timestamp,
});
// Optional: Show confirmation message
console.log(`Cleared ${data.messageCount} messages`);
// Optional: Save conversation before clearing
if (data.messageCount > 0) {
fetch("/api/save-conversation", {
method: "POST",
body: JSON.stringify({
sessionId: data.sessionId,
clearedAt: data.timestamp,
}),
});
}
});Use Cases:
- Analytics: Track how often users clear conversations
- Data retention: Save conversation history before clearing
- User feedback: Prompt for feedback when clearing after many messages
- Session recovery: Allow users to undo clear action within a time window
Error Tracking with Sentry
Send errors to Sentry:
widget.on("error", (error) => {
Sentry.captureException(new Error(error.message), {
tags: {
component: "chat-widget",
errorType: error.type,
},
extra: {
sessionId: error.sessionId,
details: error.details,
stack: error.stack,
},
});
});Event Data Reference
Common Fields
All events include:
timestamp- ISO 8601 format timestampsessionId- Current session identifier (when applicable)
Duration Fields
Duration measurements in milliseconds for:
sessionEnd.duration- Session lengthmessageComplete.duration- Response timevoiceEnd.duration- Voice input durationttsEnd.duration- TTS playback duration
Error Types
The error event includes a type field:
interceptor- Message interceptor errortimeout- Request timeoutbackend- Backend API errorfeedback- Feedback submission errorunknown- Unclassified error
Interactive Demo
See the Events System Demo to watch events fire in real-time as you interact with the widget.
Next Steps
- Interactive Demo - See events in action
- Message Interceptor - Handle messages programmatically
- Configuration - All widget options