Skip to main content

Chat Widget (JavaScript)

The Chat Widget JavaScript module allows you to quickly integrate our agents into your site. This module is designed to be as simple as possible to integrate, and it provides a simple API to interact with the agent.

Setup

To add a new instance of the chat widget, you'll need to add the following resources to your HTML file:

  • Empty HTML element that will be replaced by the component
  • Link to the JS file
  • Component initialization script
  1. Add an empty HTML element that will be replaced by the chat widget:

    <!-- Empty HTML element to be replaced by the widget -->
    <div id="aihub-chat"></div>
  2. Add the JS file to the end of the <body> section of your HTML file:

    <!-- Add JS for chat widget -->
    <script src="https://hub.serenitystar.ai/resources/chat.js"></script>
  3. Initialize the chat widget with the following script:

    <script>
    document.addEventListener("DOMContentLoaded", function () {
    const chat = new AIHubChat("aihub-chat", {
    apiKey: "<Your API key>",
    agentCode: "<Your Agent Code>",
    baseURL: "https://api.serenitystar.ai/api",
    });
    chat.init();
    });
    </script>

If you want to extract the content of current page and send the data to the agent, you can use the Content Extractor feature.

Initialization

You can initialize the chat widget by creating a new instance of the AIHubChat class and calling the init method:

const chat = new AIHubChat("aihub-chat", {
// Configuration options here
});
chat.init();

Configuration Options

The chat widget accepts the following configuration options:

{
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
logoURL: "<URL to the logo image>", // Optional. The URL to the logo image.
conversationId: "<Conversation ID>", // The ID of the conversation to load. Only required if you want to load an existing conversation.
mode: "compact", // String that indicates the chat mode (compact, floating, fullscreen, side-panel or floating-side-panel). Default is "floating"
readOnly: false, // Boolean that indicates whether the chat is read-only.
scrollToBottom: true, // Boolean that indicates whether the chat should scroll to the bottom by default. Optional. Default is true.
allowUpload: false, // Boolean that indicates whether file uploads are allowed.
maxHeight: "500px", // String that indicates the maximum height of the chat widget.
showTermsAndConditions: false, // Boolean that indicates whether the terms and conditions should be displayed.
showAcceptCommunications: false, // Boolean that indicates whether the accept communications checkbox should be displayed.
showMetaAnalysisInfo: false, // Boolean that indicates whether the meta analysis info should be displayed on each message. Optional. Default is false.
showTokenUsageInfo: false, // Boolean that indicates whether the token usage info should be displayed on each message. Optional. Default is false.
showTimeToFirstTokenInfo: false, // Boolean that indicates whether the time to first token info should be displayed on each message. Optional. Default is false.
showExecutorTaskLogsInfo: false, // Boolean that indicates whether the executor task logs info should be displayed on each message. Optional. Default is false.
storeConversation: true, // Boolean that indicates whether the conversation should be stored in local storage.
expanded: false, // Boolean that indicates whether the chat widget is expanded by default. This is only relevant in when mode is floating. Optional. Default is false.
stream: true, // Boolean that indicates whether the chat should stream responses as they are generated. Optional. Default is true.
extractPageContent: false, // Boolean that indicates whether the page content should be extracted and sent to the agent. Optional. Default is false.
showPreviousConversations: false, // Boolean that indicates whether previous conversations should be accessible by scrolling up. Optional. Default is false.
userIdentifier: "user123", // String that indicates the user identifier. Optional.
engagementMessage: {
enabled: true, // Boolean that indicates whether the engagement message should be displayed. Optional. Default is true.
message: "Hello, how can I help you?", // The message to display in the engagement message. Optional. It will show the initial message from the agent if not provided.
showAfter: 10 // The time in seconds after which the engagement message should be displayed. Optional. Default is 10 seconds.
},
locale: {
uploadFileErrorMessage: "", // The error message for file uploads.
uploadFilesErrorMessage: "", // The error message for multiple file uploads.
chatErrorMessage: "", // The error message for chat messages.
headerTitle: "<Chat header title>", // The title of the chat header.
finalizedMessage: "", // The message displayed when the chat is finalized.
chooseAnOptionMessage: "", // The message displayed when choosing an option.
limitExceededMessage: "", // The message displayed when the character limit is exceeded.
waitUntilMessage: "", // The message displayed when waiting for a response.
remainingMessage: "", // The message displayed when there are remaining characters.
termsAndConditionsMessage: "<Your terms and conditions message>", // The terms and conditions message.
acceptCommunicationsMessage: "<Your accept communications message>", // The accept communications message.
inputPlaceholder: "write your message here...", // The placeholder text for the chat input.
metaAnalysisTitle: "Meta Analysis", // The title for the meta analysis card on each message.
completionUsageTitle: "Completion Usage", // The title for the token completion usage card on each message.
timeToFirstTokenTitle: "Time to first token", // The title for the time to first token tooltip.
newChatBtnMessage: "New Chat", // The message for the "New chat" button in the header. (Only visible when storeConversation is true)
executorTaskLogsTitle: "Executor Task Logs" // The title for the executor task logs card on each message.
millisecondsUnit: "ms", // The unit for milliseconds,
exceededMaxNumberOfFilesMessage: "You can only upload up to 5 files per message", // The message displayed when the user tries to upload more than the allowed number of files
exceededMaxFileStatusChecksMessage: "It seems that the file upload is taking longer than expected. Please try again later.", // Message to display when files are taking longer than expected to upload
chatInitConversationErrorMessage: "Error initializing conversation", // The error message to display when there is an error initializing the conversation
closedConversationMessage: "", // The message displayed when the conversation has been closed.
loadingPreviousChatMessage: "Loading previous chat...", // The message displayed while loading a previous conversation. Optional.
endOfChatHistoryMessage: "End of chat history", // The message displayed at the boundary between previous conversations and the current one. Optional.
loadPreviousChatButtonMessage: "Load previous chat", // The label for the button that loads a previous conversation. Optional.
newChatSeparatorMessage: "New Chat", // The text displayed on the separator between conversations when a new chat is started. Optional.
},
theme: {
header: {
bgColor: "<Header background color>",
textColor: "<Text color for the header>",
resetChatBtn: {
bgColor: "<Background color for reset button>",
hoverBgColor: "<Hover background color for reset button>",
textColor: "<Text color for reset button>",
},
minimizeBtn: {
iconStrokeColor: "<Line Stroke color for the icon in the minimize button>"
}
},
fabButton: {
bgColor: "<Background color for the fab button>",
iconStrokeColor: "<Line Stroke color for the icon in the fab button>",
buttonSize: "<Floating action button size>", // Optional. Default is 50
iconSize: "<Floating action button icon size>", // Optional. Default is 25
},
sendButton: {
bgColor: "<Background color for the send button>",
iconStrokeColor: "<Line Stroke color for the icon in the send button>"
},
attachments: {
borderColor: "<Border color for attachments container>",
},
engagementMessage: {
bgColor: "<Background color for the engagement message>",
textColor: "<Text color for the engagement message>",
},
uploadFileBtn: {
iconStrokeColor: "<Line Stroke color for the icon in the upload file button>"
},
messageBubble: {
user: {
bgColor: "<Background color for user message bubble>",
textColor: "<Text color for user message bubble>",
},
assistant: {
bgColor: "<Background color for assistant message bubble>",
textColor: "<Text color for assistant message bubble>",
},
},
conversationStarters: {
bgColor: "<Background color for conversation starters>",
textColor: "<Text color for conversation starters>",
containerBgColor: "<Background color for conversation starters container>",
initialMessageBgColor: "<Background color for initial message in conversation starters>",
initialMessageTextColor: "<Text color for initial message in conversation starters>",
},
scrollToBottomIndicator: {
bgColor: "<Background color for scroll to bottom indicator>",
iconStrokeColor: "<Line Stroke color for the icon in the scroll to bottom indicator>"
}
},
// Optional input parameters object to send to the agent
inputParameters: {
name: "John Doe",
email: "[email protected]",
}
};

Presentation modes

You can choose between different presentation modes for the chat widget. We have 2 inline modes and 3 floating modes available:

Inline modes

  • compact: The chat widget will be displayed as a box. This is useful when you want to manually position the chat widget on your page. Compact mode

  • fullscreen: Similar to compact mode, but the chat widget will take up the entire screen.

Floating modes

Floating modes will always show a floating button on the bottom right corner of the page. When clicked, the chat widget will be displayed in different ways:

  • side-panel: The chat widget will be displayed as a side panel on the right side of the page, pushing the content to the left. Side panel mode

  • floating: The chat widget will be displayed as a floating chat window. (Default) Floating mode

  • floating-side-panel: The chat widget will be displayed as a floating side panel on the right side of the page. It won't push the content to the left. Floating Side panel mode

Stream / Single shot responses

You can choose between streaming responses as they are generated, or get a single-shot response at the end. By default, the chat widget will stream responses as they are generated. If you want to get a single-shot response at the end, you can set the stream option to false.

const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
stream: false, // Get a single-shot response at the end
});

Multiple instances on the same page

The chat widget supports multiple instances on the same page. You can create multiple instances of the chat widget by providing a unique ID for each instance:

<!-- These ids will be used to identify each instance -->
<div id="chat-1"></div>
<div id="chat-2"></div>
const chat1 = new AIHubChat("chat-1", {
...
});

const chat2 = new AIHubChat("chat-2", {
...
});

Keeping the conversation alive

By default, the chat widget will store the current conversation so that the user can continue the conversation even after refreshing the page.

If you want to start a new chat every time the page is refreshed, you can disable this behavior by setting the storeConversation option to false.

const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
storeConversation: false, // Do not store the conversation
});

When storeConversation is set to true (by default), the header of the chat widget will display a "New Chat" button that allows the user to start a new chat.

You can even keep multiple conversations alive by providing a unique key for each conversation (as mentioned above):

<div id="chat-1"></div>
<div id="chat-2"></div>
const chat1 = new AIHubChat("chat-1", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
storeConversation: true, // Store the conversation in local storage
});

const chat2 = new AIHubChat("chat-2", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
storeConversation: true, // Store the conversation in local storage
});

Previous conversations

When showPreviousConversations is enabled, the chat widget stores all past conversation IDs in local storage. As the user scrolls up in the chat body, previous conversations are lazily loaded (most-recent-first) and rendered read-only above the current chat, separated by visual dividers.

const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
showPreviousConversations: true,
});
chat.init();

How it works

  1. Every time a new conversation is created, its ID is persisted in local storage.
  2. When the user clicks "New Chat", the current conversation is finalized and a new one begins.
  3. As the user scrolls to the top of the chat body, previous conversations are loaded one at a time (most recent first) and displayed above the current messages.
  4. A visual separator with the text configured in locale.endOfChatHistoryMessage marks the boundary between previous conversations and the current one.
  5. While a previous conversation is being fetched, a loading indicator with the text configured in locale.loadingPreviousChatMessage is shown at the top of the chat.

Interaction with storeConversation

showPreviousConversations works independently of storeConversation. Even when storeConversation is false (meaning the current conversation is not persisted across page reloads), the previous conversation IDs are still stored. This allows the user to chat, reload the page, start a new conversation, and then scroll up to access their previous conversations.

Multiple instances

When using multiple chat widget instances on the same page, each instance stores its previous conversations separately using its own instance-scoped local storage key.

Edge cases

ScenarioBehavior
No previous conversations existScroll-to-top detection is skipped; nothing is rendered above the current chat.
All previous conversations already loadedScroll-to-top detection becomes a no-op; the "End of chat history" separator is displayed.
A previous conversation cannot be found (404)The entry is skipped and the next one is loaded.
Agent code or base URL changedPrevious conversation history is cleared to avoid loading chats from a different agent.

Customization

There are certain options available to customize the look and feel & general behavior of the chat widget:

  • maxHeight: The maximum height of the chat widget. This value will be ignored if the chat is in fullscreen mode.
  • mode: The mode of the chat widget. The available options are compact, floating, and fullscreen, side-panel, and floating-side-panel. Default is floating.
    • compact: The chat widget will be displayed as a box. This is useful when you want to manually position the chat widget on your page.
    • floating: The chat widget will be displayed as a floating button in the bottom right corner of the page. This is useful when you want the chat widget to be easily accessible. Once clicked, the chat will be displayed as a floating chat window (Default).
    • fullscreen: The chat widget will be displayed as a fullscreen chat window. This is useful when you want the chat widget to take up the entire screen.
    • side-panel: The chat widget will be displayed as a side panel on the right side of the page, pushing the content to the left.
    • floating-side-panel: The chat widget will be displayed as a floating side panel on the right side of the page. It won't push the content to the left.
  • readOnly: Boolean that indicates whether the chat is read-only. When set to true, the chat will not accept any user input.
  • scrollToBottom: Boolean that indicates whether the chat should scroll to the bottom by default. Optional. Default is true.
  • allowUpload: Boolean that indicates whether file uploads are allowed.
  • stream: Boolean that indicates whether the chat should stream responses as they are generated. Default is true.
  • showTermsAndConditions: Boolean that indicates whether the terms and conditions should be displayed before starting the chat.
  • showAcceptCommunications: Boolean that indicates whether the accept communications checkbox should be displayed before starting the chat.
  • expanded: Boolean that indicates whether the chat widget is expanded by default. This is only relevant in when mode is floating, side-panel, or floating-side-panel. Optional. Default is false.
  • extractPageContent: Boolean that indicates whether the page content should be extracted and sent to the agent. Optional. Default is false.
  • showPreviousConversations: Boolean that indicates whether previous conversations should be accessible by scrolling up in the chat body. When enabled, past conversation IDs are stored in local storage and their messages are lazily loaded as the user scrolls to the top. Optional. Default is false. Click here to learn more.
  • inputParameters: Optional input parameters object to send to the agent. Click here to learn more.
  • userIdentifier: String that indicates the user identifier. This will be sent to the agent only when the conversation starts. Optional.
  • engagementMessage: Object that contains the configuration for the engagement message. Click here to learn more.
  • logoURL: The URL to the logo image. Optional.

Message metadata

You can optionally show metadata for each message, such as meta analysis, token completion usage, and time to first token. All this information is optional and can be enabled/disabled using the following options:

const chat = new AIHubChat("aihub-chat", {
..., // Other options
showMetaAnalysisInfo: true, // Show meta analysis info on each message
showTokenUsageInfo: true, // Show token usage info on each message
showTimeToFirstTokenInfo: true, // Show time to first token info on each message
showExecutorTaskLogsInfo: true, // Show executor task logs info on each message
});

Localization

You can customize the text displayed in the chat widget by providing a locale object with the following properties:

  • uploadFileErrorMessage: The error message for file uploads.
  • uploadFilesErrorMessage: The error message for multiple file uploads.
  • chatErrorMessage: The error message for chat messages.
  • headerTitle: The title of the chat header.
  • finalizedMessage: The message displayed when the chat is finalized.
  • chooseAnOptionMessage: The message displayed when choosing an option.
  • limitExceededMessage: The message displayed when the character limit is exceeded.
  • waitUntilMessage: The message displayed when waiting for a response.
  • remainingMessage: The message displayed when there are remaining characters.
  • termsAndConditionsMessage: The terms and conditions message.
  • acceptCommunicationsMessage: The accept communications message.
  • inputPlaceholder: The placeholder text for the chat input.
  • metaAnalysisTitle: The title for the meta analysis card on each message.
  • completionUsageTitle: The title for the token completion usage card on each message.
  • newChatBtnMessage: The message for the "New chat" button in the header. (Only visible when storeConversation is true)
  • timeToFirstTokenTitle: The title for the time to first token tooltip.
  • executorTaskLogsTitle: The title for the executor task logs card on each message.
  • millisecondsUnit: The unit for milliseconds.
  • exceededMaxNumberOfFilesMessage: The message displayed when the user tries to upload more than the allowed number of files.
  • exceededMaxFileStatusChecksMessage: Message to display when files are taking longer than expected to upload.
  • chatInitConversationErrorMessage: The error message to display when there is an error initializing the conversation.
  • closedConversationMessage: The message displayed when the conversation has been closed by the server.
  • loadingPreviousChatMessage: The message displayed while loading a previous conversation. Default is "Loading previous chat...".
  • endOfChatHistoryMessage: The message displayed at the boundary between previous conversations and the current one. Default is "End of chat history".
  • loadPreviousChatButtonMessage: The label for the button that loads a previous conversation. Default is "Load previous chat".
  • newChatSeparatorMessage: The text displayed on the separator between conversations when a new chat is started. Default is "New Chat".
const chat = new AIHubChat("aihub-chat", {
..., // Other options
locale: {
uploadFileErrorMessage: "Error uploading file",
uploadFilesErrorMessage: "Error uploading files",
chatErrorMessage: "Error sending message",
headerTitle: "Chat with us",
finalizedMessage: "Chat finalized",
chooseAnOptionMessage: "Choose an option",
limitExceededMessage: "Character limit exceeded",
waitUntilMessage: "Please wait until we respond",
remainingMessage: " characters remaining",
termsAndConditionsMessage: "By using this chat, you agree to our terms",
acceptCommunicationsMessage: "I agree to receive communications",
inputPlaceholder: "Write your message here...",
newChatBtnMessage: "New Chat",
timeToFirstTokenTitle: "Time to first token",
executorTaskLogsTitle: "Executor Task Logs",
millisecondsUnit: "ms",
exceededMaxNumberOfFilesMessage: "You can only upload up to 5 files per message",
exceededMaxFileStatusChecksMessage: "It seems that the file upload is taking longer than expected. Please try again later.",
chatInitConversationErrorMessage: "Error initializing conversation",
closedConversationMessage: "This conversation has ended.",
loadingPreviousChatMessage: "Loading previous chat...",
endOfChatHistoryMessage: "End of chat history",
loadPreviousChatButtonMessage: "Load previous chat",
newChatSeparatorMessage: "New Chat",
},
});

Theme

You can customize the look and feel of the chat widget by providing a theme object with the following properties:

The theme object is optional. If not provided, the default theme will be used.

const chat = new AIHubChat("aihub-chat", {
...
theme: {
header: {
bgColor: "<Header background color>",
textColor: "<Text color for the header>",
resetChatBtn: {
bgColor: "<Reset chat button background color>",
hoverBgColor: "<Hover background color for reset chat button>",
textColor: "<Reset chat button text color>",
},
minimizeBtn: {
iconStrokeColor: "<Line Stroke color for the icon in the minimize button>"
},
},
fabButton: {
bgColor: "<Floating action button background color>",
iconStrokeColor: "<Line Stroke color for the icon in the fab button>",
buttonSize: "<Floating action button size>", // Optional. Default is 50
iconSize: "<Floating action button icon size>", // Optional. Default is 25
},
sendButton: {
bgColor: "<Send button background color>",
iconStrokeColor: "<Line Stroke color for the icon in the send button>"
},
attachments: {
borderColor: "<Border color for attachments container>",
},
engagementMessage: {
bgColor: "<Background color for the engagement message>",
textColor: "<Text color for the engagement message>",
},
uploadFileBtn: {
iconStrokeColor: "<Line Stroke color for the icon in the upload file button>"
},
messageBubble: {
user: {
bgColor: "<Background color for user message bubble>",
textColor: "<Text color for user message bubble>",
},
assistant: {
bgColor: "<Background color for assistant message bubble>",
textColor: "<Text color for assistant message bubble>",
},
},
conversationStarters: {
bgColor: "<Background color for conversation starters>",
textColor: "<Text color for conversation starters>",
containerBgColor: "<Background color for conversation starters container>",
initialMessageBgColor: "<Background color for initial message in conversation starters>",
initialMessageTextColor: "<Text color for initial message in conversation starters>",
},
scrollToBottomIndicator: {
bgColor: "<Background color for scroll to bottom indicator>",
iconStrokeColor: "<Line Stroke color for the icon in the scroll to bottom indicator>"
}
}
})

CSS Styling Considerations

Default build (Shadow DOM)

The default chat widget uses Shadow DOM, which fully isolates the widget's styles from your page. No special CSS precautions are needed — your page styles will not affect the widget, and the widget's styles will not leak into your page.

If you need to adjust the widget's appearance, use the theme configuration object.

Customizable build (no Shadow DOM)

If you are using the customizable build, the widget renders without Shadow DOM and exposes stable sc-* semantic CSS classes for overriding styles. See the Customizable Build section for full setup and usage details.

Page CSS can affect the widget

Because the customizable build does not use Shadow DOM, overly broad CSS selectors on your page can unintentionally break the widget. Keep these guidelines in mind:

  • Avoid generic selectors like *, div, button, input, p — scope them to your own containers instead (e.g., .my-page-content button)
  • Don't use !important in global styles — the widget's baseline CSS does not use !important, so generic !important rules will override it unexpectedly
  • Watch for box-sizing resets — the widget relies on box-sizing: border-box
  • Be mindful of z-index — very high z-index values in your styles might cover the widget's floating elements
  • Test the widget after making global CSS changes to ensure it still renders correctly

Loading an existing conversation

You can load an existing conversation by using the conversationId option like this:

const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
conversationId: "<Conversation ID>", // <-- Required
});

Extracting the page content

We have a content extractor that can be used to extract the content from the current page and send it to the agent.

This way, you can easily improve the agent response by providing more context about the user's current page.

To use the content extractor, use the extractPageContent flag like this:

<script>
document.addEventListener("DOMContentLoaded", function () {
const chat = new AIHubChat("aihub-chat", {
apiKey: "API_KEY",
agentCode: "AGENT_CODE",
baseURL: "https://api.serenitystar.ai/api",
extractPageContent: true,
});
chat.init();
});
</script>

Input parameters

You can optionally provide input parameters to the agent by using the inputParameters option. This can be useful for providing additional context to the agent.

const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
inputParameters: {
name: "John Doe",
email: "[email protected]",
},
});

These input parameters will be sent to the agent both at the start of the conversation and with each message.

Agent Response Callbacks

You can capture each response from the agent by providing an onAgentResponse callback function. This allows you to process or use the agent's response data in your application.

const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
onAgentResponse: (response) => {
console.log("Agent response:", response);
// Process the response data as needed
}
});

The response object contains detailed information about the agent's response, including:

  • content: The text content of the agent's response
  • json_content: Any JSON content that might be returned (null if not applicable)
  • completion_usage: Token usage statistics for the response
    • completion_tokens: Number of tokens used for the completion
    • prompt_tokens: Number of tokens used for the prompt
    • total_tokens: Total number of tokens used
  • action_results: Results of any actions performed by the agent
  • meta_analysis: Any meta analysis data (null if not applicable)
  • time_to_first_token: Time in milliseconds until the first token was received
  • instance_id: Unique identifier for the response instance
  • executor_task_logs: Detailed logs of the execution tasks, including:
    • description: Description of the task
    • duration: Duration of the task in milliseconds
    • success: Boolean indicating if the task was successful

Example response object:

{
"content": "To create a contact form in HTML, you'll need to use the <form> element along with various input fields. Here's a basic example:\n\n```html\n<form action=\"submit_form.php\" method=\"POST\">\n <div>\n <label for=\"name\">Name:</label>\n <input type=\"text\" id=\"name\" name=\"name\" required>\n </div>\n \n <div>\n <label for=\"email\">Email:</label>\n <input type=\"email\" id=\"email\" name=\"email\" required>\n </div>\n \n <div>\n <label for=\"message\">Message:</label>\n <textarea id=\"message\" name=\"message\" rows=\"5\" required></textarea>\n </div>\n \n <button type=\"submit\">Submit</button>\n</form>\n```\n\nYou can style this form using CSS and add JavaScript for validation. Remember to implement proper server-side processing for the form submission.",
"json_content": null,
"completion_usage": {
"completion_tokens": 182,
"prompt_tokens": 125,
"total_tokens": 307
},
"action_results": {},
"meta_analysis": null,
"time_to_first_token": 1520,
"instance_id": "675e9321-4b76-c3e8-f5a2-9c41b7d32e45",
"executor_task_logs": [
{
"description": "Loading configuration",
"duration": 18,
"success": true
},
{
"description": "Validating configuration",
"duration": 7,
"success": true
},
{
"description": "Calculating remaining token budget",
"duration": 45,
"success": true
},
{
"description": "Appending context",
"duration": 39,
"success": true
},
{
"description": "Getting AI response",
"duration": 2458,
"success": true
},
{
"description": "Calculating prices",
"duration": 3,
"success": true
},
{
"description": "Updating chat",
"duration": 10,
"success": true
}
]
}

This callback is particularly useful for applications that need to:

  • Track and analyze agent responses
  • Extract specific data from responses
  • Trigger additional actions based on agent responses
  • Log conversation metrics

Engagement message

By default, the chat widget will display an engagement message (popup) after a certain amount of time.

This message is intended to engage the user and encourage them to start a conversation with the agent.

You can customize the engagement message by providing an engagementMessage object with the following properties:

  • enabled: Boolean that indicates whether the engagement message should be displayed. Optional. Default is true.
  • message: The message to display in the engagement message. Optional. It will show the initial message from the agent if not provided.
  • showAfter: The time in seconds after which the engagement message should be displayed. Optional. Default is 10 seconds.
const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
engagementMessage: {
enabled: true,
message: "Hello, how can I help you?",
showAfter: 10, // seconds
},
});

If the message is not provided, the initial message from the agent will be displayed instead.

Error Handling

The chat widget handles errors gracefully and displays appropriate messages to users when something goes wrong. Depending on the type of error, the widget will show a message in the chat, display a countdown timer, or disable the input.

Error scenarios

ScenarioWhat the user seesChat input behavior
Rate limit exceededA countdown timer indicating when messages can be sent again.Disabled until the countdown expires.
Conversation closedAn informational message indicating the conversation has ended.Permanently disabled.
Quota or balance exceededA generic error message.Re-enabled for retry.
Validation errorsThe specific error details returned by the server.Re-enabled for retry.
Network or unexpected errorsA generic error message.Re-enabled for retry.
Streaming errorsThe server-provided error message, or a generic fallback.Re-enabled for retry.

Error display styles

Errors are displayed differently depending on their nature:

  • Error messages appear as chat bubbles with error styling (e.g., quota exceeded, network failures, streaming errors).
  • Info messages appear with informational styling (e.g., conversation closed).
  • Countdown timer replaces the chat input area when a rate limit is hit, showing a timer until the user can send messages again.

Customizing error messages

You can customize the error messages shown to users through the locale configuration:

const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
locale: {
chatErrorMessage: "Something went wrong. Please try again.",
closedConversationMessage: "This conversation has ended.",
limitExceededMessage: "You've reached the message limit.",
waitUntilMessage: "Please wait until",
},
});
PropertyDescription
chatErrorMessageGeneric fallback message for most error types. Defaults to "Unexpected error" if not set.
closedConversationMessageMessage shown when the conversation has been closed by the server.
limitExceededMessageText displayed alongside the countdown timer when a rate limit is hit.
waitUntilMessageText displayed alongside the countdown timer to indicate when the user can retry.
info

If no custom error messages are configured through locale, the widget will fall back to "Unexpected error" as the default message.

Usage examples

Basic usage

const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
});
chat.init();

Read-only mode

const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
readOnly: true,
messages: [
{
sender: "bot",
createdAt: "2024-04-26T12:00:00Z",
type: "text",
value: "Hello, how can I help you?",
},
{
sender: "user",
createdAt: "2024-04-26T12:01:00Z",
type: "text",
value: "Hello, I have a question about cooking recipes",
},
{
sender: "bot",
createdAt: "2024-04-26T12:02:00Z",
type: "text",
value: "Sure, what would you like to know?",
},
],
});

Customizable Build (Advanced CSS Overrides)

The default chat widget uses Shadow DOM to isolate its styles from your page. This is ideal for most use cases, but it prevents you from overriding the widget's internal CSS.

For clients who need full control over the widget's appearance, we provide an alternative customizable build that renders without Shadow DOM and ships styles as a separate CSS file. This allows you to override any style using standard CSS selectors.

Important

The customizable build intentionally exposes the widget's internals to your page CSS. This means global styles on your page can affect the widget. Use this build only when you need deep CSS customization and are prepared to manage potential style conflicts.

Setup

Replace the default resources with the customizable ones:

<!-- 1. Widget CSS (load first) -->
<link
rel="stylesheet"
href="https://hub.serenitystar.ai/resources/chat-customizable.css"
/>

<!-- 2. Your CSS overrides (load after the widget CSS) -->
<link rel="stylesheet" href="your-overrides.css" />

<!-- 3. Widget container -->
<div id="aihub-chat"></div>

<!-- 4. Widget JS -->
<script src="https://hub.serenitystar.ai/resources/chat-customizable.js"></script>

<!-- 5. Initialize (same API as the default build) -->
<script>
document.addEventListener("DOMContentLoaded", function () {
const chat = new AIHubChat("aihub-chat", {
apiKey: "<Your API key>",
agentCode: "<Your Agent Code>",
baseURL: "https://api.serenitystar.ai/api",
});
chat.init();
});
</script>

How it works

  • The widget renders in the light DOM (no Shadow DOM), so your CSS rules can reach its internal elements.
  • The widget's own stylesheet uses all: initial (without !important) on the root element, giving it a clean baseline.
  • Because !important is not used, any CSS you load after the widget's stylesheet wins naturally via the cascade — no need for !important on your side.

Overriding styles with semantic classes

Every meaningful element in the widget has a stable semantic CSS class prefixed with sc- (serenity chat). These classes carry no styles by themselves — they exist purely as hooks for your overrides.

/* your-overrides.css */

/* Change header appearance */
.sc-header {
background: #1a1a2e;
border-radius: 0;
}

/* Style user message bubbles */
.sc-message-bubble--user {
background: #1a73e8;
color: white;
border-radius: 16px 16px 4px 16px;
}

/* Style agent message bubbles */
.sc-message-bubble--agent {
background: white;
border: 1px solid #e0e0e0;
border-radius: 16px 16px 16px 4px;
}

/* Customize the send button */
.sc-footer-send-btn {
background: #1a73e8;
border-radius: 50%;
}

/* Customize the floating action button */
.sc-fab-btn {
width: 64px;
height: 64px;
background: #1a73e8;
}

Naming convention

PatternExampleMeaning
sc-{component}sc-header, sc-footerComponent root
sc-{component}-{element}sc-header-title, sc-footer-textareaChild element
sc-{component}--{variant}sc-message-bubble--userVariant/modifier

Default build vs. Customizable build

FeatureDefault buildCustomizable build
Files neededchat.js onlychat-customizable.js + chat-customizable.css
Shadow DOMYes (styles fully isolated)No (styles exposed to page CSS)
CSS overridesOnly via theme config optionsFull CSS control via sc-* classes
Risk of style conflictsNoneClient accepts this trade-off
Configuration APIIdenticalIdentical
Semantic class stability

The sc-* classes are treated as a public API. They follow semantic versioning — class names will not change in minor or patch releases.

For a complete list of all available semantic classes, see the Appendix: Semantic CSS Classes Reference at the end of this page.


Appendix: Semantic CSS Classes Reference

Below is the complete list of semantic CSS classes available in the customizable build, grouped by component. Use these classes in your override stylesheet to target specific elements.

Layout

Chat (root wrapper)

ClassElement
sc-rootOutermost wrapper
sc-wrapperPositioned container
sc-wrapper-innerAnimated inner wrapper
sc-chatMain chat card
sc-fab-areaFAB + engagement message container
ClassElement
sc-headerHeader root
sc-header--side-panelSide-panel header variant
sc-header-actionsTop buttons row (side-panel mode)
sc-header-contentLogo + title area (side-panel mode)
sc-header-logoLogo image
sc-header-titleTitle text
sc-header-toggle-btnMinimize / expand button
sc-header-expand-btnExpand to side-panel button
sc-header-reset-btnNew chat / reset button

Body

ClassElement
sc-bodyScrollable outer area
sc-body-innerInner content wrapper
sc-messagesMessage list container
sc-prev-chats-loadingLoading indicator for previous chats
sc-messages-load-prev-btn"Load previous chat" button
sc-messages-typing-indicatorTyping animation wrapper
sc-messages-countdownRate-limit countdown wrapper
ClassElement
sc-footerFooter root container
sc-footer-input-wrapperInput area wrapper (textarea + buttons)
sc-footer-textareaText input
sc-footer-mic-btnMicrophone / audio recording button
sc-footer-upload-btnFile upload button
sc-footer-file-inputHidden file input
sc-footer-send-btnSend message button
sc-footer-stop-btnStop streaming button
sc-footer-finalizedEnd-of-conversation message
sc-footer-attachmentsAttachments scroll area
sc-footer-upload-errorFile validation error
sc-footer-disclaimerDisclaimer wrapper
sc-footer-disclaimer-textDisclaimer text
sc-footer-disclaimer-toggle"See more" / "See less" toggle

Messages

MessageBox

ClassElement
sc-message-wrapperMessage row wrapper
sc-message-wrapper--agentAgent message row
sc-message-wrapper--userUser message row
sc-message-bubbleMessage bubble
sc-message-bubble--agentAgent bubble variant
sc-message-bubble--userUser bubble variant
sc-message-bubble--errorError message variant
sc-message-bubble--infoInfo message variant
sc-message-textMessage text content
sc-message-audioAudio message player
sc-message-timeMessage timestamp

Message Metadata

ClassElement
sc-message-metadataMetadata row
sc-message-metadata-timeTimestamp
sc-message-metadata-iconsAction icons row
sc-message-metadata-analysisMeta analysis button
sc-message-metadata-usageToken usage button
sc-message-metadata-logsExecutor logs button
sc-message-metadata-ttftTime-to-first-token indicator

Message Pending Actions

ClassElement
sc-pending-actionsActions container
sc-pending-actionSingle action card
sc-pending-action-iconConnector icon
sc-pending-action-nameConnector name
sc-pending-action-connect-btnConnect button

Message Skills

ClassElement
sc-message-skillsSkills container
sc-message-skillIndividual skill

Inline Attachments

ClassElement
sc-inline-attachmentsAttachments list
sc-inline-attachmentSingle attachment
sc-inline-attachment-iconFile icon
sc-inline-attachment-infoFile name + details
sc-inline-attachment-sizeFile size

Feedback

ClassElement
sc-feedbackFeedback container
sc-feedback-thumbs-upThumbs-up button
sc-feedback-thumbs-downThumbs-down button

Interactive Components

Floating Action Button (FAB)

ClassElement
sc-fab-btnFloating action button

Conversation Starters

ClassElement
sc-startersRoot container
sc-starters-welcomeWelcome message container
sc-starters-welcome-logoWelcome logo
sc-starters-welcome-textWelcome text
sc-starters-buttonsButtons wrapper
sc-starters-btnIndividual starter button

Engagement Message

ClassElement
sc-engagementPopup wrapper
sc-engagement-cardMessage card
sc-engagement-closeClose button
sc-engagement-textMessage text
sc-engagement-arrowArrow / triangle pointer

Scroll To Bottom Indicator

ClassElement
sc-scroll-indicatorOuter container
sc-scroll-indicator-btnClickable button

Audio Recorder

ClassElement
sc-audio-recorderRoot container
sc-audio-recorder-innerInner container
sc-audio-recorder-errorPermission error container
sc-audio-recorder-error-textError text
sc-audio-recorder-statusRecording status message
sc-audio-recorder-actionsButtons container
sc-audio-recorder-cancel-btnCancel button
sc-audio-recorder-pause-btnPause / resume button
sc-audio-recorder-send-btnSend recording button
sc-audio-recorder-uploadingUploading indicator

File Attachments

ClassElement
sc-attachmentsAttachments list
sc-attachmentSingle attachment
sc-attachment--errorAttachment with error
sc-attachment--processedSuccessfully processed attachment
sc-attachment-iconFile icon container
sc-attachment-infoFile name + details
sc-attachment-tagsTags wrapper
sc-attachment-sizeFile size tag
sc-attachment-error-tagError tag
sc-attachment-actionsRemove / action buttons
sc-attachments-max-error"Max files exceeded" error
sc-attachments-status-errorFile status check error

Pre-conditions

Terms and Conditions

ClassElement
sc-termsContainer
sc-terms-checkboxCheckbox input
sc-terms-textTerms text

Accept Communications

ClassElement
sc-accept-commsContainer
sc-accept-comms-checkboxCheckbox input
sc-accept-comms-textCommunications text

Pre-conditions Wrapper

ClassElement
sc-preconditionsRoot container
sc-preconditions-termsTerms section wrapper
sc-preconditions-commsCommunications section wrapper

Utilities

Countdown Timer

ClassElement
sc-countdownTimer container
sc-countdown-titleLimit exceeded message
sc-countdown-subtitle"Wait until" message
sc-countdown-timeTime remaining display

Previous Chats

ClassElement
sc-prev-chatsRoot container
sc-prev-chatSingle previous chat group
sc-prev-chat-separatorSeparator between chats
sc-prev-chat-separator-lineSeparator line
sc-prev-chat-separator-textSeparator text
sc-prev-chats-end-separatorEnd-of-history separator

Realtime Voice

ClassElement
sc-realtimeRoot container
sc-realtime-avatarAgent avatar image
sc-realtime-greetingGreeting heading
sc-realtime-wavesurfWaveform wrapper
sc-realtime-errorError message
sc-realtime-actionsAction buttons container
sc-realtime-start-btnStart conversation button
sc-realtime-loadingLoading indicator
sc-realtime-mute-btnMute / unmute button
sc-realtime-stop-btnStop conversation button

Loading Indicators

ClassElement
sc-dotsAnimated dots container
sc-dotIndividual dot
sc-three-dotsThree-dots typing wrapper
sc-spinnerSpinner container
sc-spinner-rollerSpinner roller element

Audio Player

ClassElement
sc-audio-playerAudio element