This page covers the HyperHelp widget's JavaScript API. If you just need to install the widget, start with the installation guide.
Initialization#
The widget is initialized via window.HyperhelpWidget.init():
<script
src="https://app.hyperhelp.ai/widget/chat.js"
async
onload='window.HyperhelpWidget.init({
"chatbotKey": "YOUR_WIDGET_KEY"
})'
></script>
Init options#
| Option | Type | Description |
|---|---|---|
chatbotKey |
string |
Required. Your chatbot's widget key. |
copy |
object |
Override any widget text string. See Copy overrides. |
styleOverrides |
object |
Inline style overrides per widget element. See Style overrides. |
customCss |
string |
Raw CSS injected into the widget's Shadow DOM. See Custom CSS. |
customCssUrl |
string |
URL to an external CSS file injected into the widget's Shadow DOM. |
callbacks |
object |
Event callback functions. See Callbacks. |
Widget API#
init() returns a widget instance with programmatic control:
const widget = window.HyperhelpWidget.init({
chatbotKey: "YOUR_WIDGET_KEY"
});
// Open the widget programmatically
widget.open();
// Close the widget
widget.close();
Common use cases:
- Open the widget after a delay:
setTimeout(() => widget.open(), 5000) - Open on a custom button click:
document.getElementById('help-btn').addEventListener('click', () => widget.open())
Copy overrides#
Override any widget string via the copy option. You only need to include the keys you want to change — unset keys fall back to the dashboard defaults.
window.HyperhelpWidget.init({
chatbotKey: "YOUR_WIDGET_KEY",
copy: {
title: "Ask Nova",
subtitle: "Help from our docs",
badge_label: "Chat",
composer_placeholder: "Ask about pricing...",
composer_send: "Send",
empty_title: "Start the conversation",
empty_description: "Ask your first question to see answers.",
assistant_label: "Assistant",
user_label: "You",
sources_title: "Sources",
disclaimer: "Answers are generated from this site.",
error: "Something went wrong, please contact the site owner.",
unavailable: "Something is not working as it should. Please contact the site owner.",
close_label: "Close",
new_conversation_label: "New conversation",
copy_label: "Copy",
copied_label: "Copied",
code_copy_label: "Copy code",
citation_label: "Source :number: :title",
scroll_to_latest_label: "Scroll to latest",
lead_close_label: "Close lead form",
lead_submit_label: "Submit",
lead_success: "Thanks! Your details have been submitted."
}
});
Tip: You don't need to override every key — only the ones you want to change. Unset keys fall back to the dashboard defaults.
Style overrides#
Pass inline style objects (camelCase CSS properties) to customize specific widget elements without writing CSS:
window.HyperhelpWidget.init({
chatbotKey: "YOUR_WIDGET_KEY",
styleOverrides: {
panel: { boxShadow: "0 24px 60px rgba(15, 23, 42, 0.18)" },
openButton: { backgroundColor: "#0f172a" },
userBubble: { backgroundColor: "#4338ca", color: "#fff" },
header: { borderBottom: "2px solid #e5e7eb" }
}
});
Available style override keys#
Layout
| Key | What it styles |
|---|---|
panel |
The main chat panel container |
header |
Panel header bar |
title |
Header title text |
subtitle |
Header subtitle text |
body |
Message area |
footer |
Panel footer |
Messages
| Key | What it styles |
|---|---|
emptyState |
Empty conversation state |
error |
Error message display |
messageRow |
Individual message row |
messageGroup |
Grouped messages wrapper |
userBubble |
Visitor's message bubble |
assistantBubble |
Bot's message bubble |
Sources
| Key | What it styles |
|---|---|
sources |
Sources section container |
sourcesLabel |
"Sources" heading, which doubles as the open/close toggle |
sourcesList |
Sources link list |
sourcesLink |
Individual source link |
Composer
| Key | What it styles |
|---|---|
composer |
Message input area container |
composerInput |
Text input field |
composerButton |
Send button |
Open button
| Key | What it styles |
|---|---|
openButton |
Floating open button |
openDot |
Pulse indicator dot |
badgeLabel |
Button badge text |
Lead capture
| Key | What it styles |
|---|---|
leadRow |
Lead form row container |
leadCard |
Lead form card |
leadHeadline |
Lead form headline |
leadDescription |
Lead form description |
leadFieldLabel |
Form field label |
leadInput |
Form input field |
leadError |
Field validation error |
leadSubmitButton |
Form submit button |
Custom CSS#
For full CSS control — hover states, media queries, animations — inject CSS directly into the widget's Shadow DOM.
Inline CSS:
window.HyperhelpWidget.init({
chatbotKey: "YOUR_WIDGET_KEY",
customCss: `
.widget-panel {
border: 2px solid #e5e7eb;
}
.widget-message-user {
background: linear-gradient(135deg, #667eea, #764ba2);
}
.widget-open-button:hover {
transform: scale(1.1);
}
`
});
External stylesheet:
window.HyperhelpWidget.init({
chatbotKey: "YOUR_WIDGET_KEY",
customCssUrl: "https://example.com/widget-overrides.css"
});
CSS class hooks#
These stable CSS classes are available for targeting in custom CSS. Classes prefixed with widget- are part of the public API and won't change between releases.
Layout:
.widget-panel, .widget-header, .widget-title, .widget-subtitle, .widget-body, .widget-footer
Messages:
.widget-message-row, .widget-message-group, .widget-message-user, .widget-message-assistant, .widget-message-copy, .widget-scroll-latest (the button that appears when the conversation continues below the visible area)
Sources:
.widget-sources, .widget-sources-trigger (the heading, which doubles as the show/hide toggle), .widget-sources-label, .widget-sources-panel, .widget-sources-list, .widget-sources-link (one reference row), .widget-sources-icon (the number tile on a row), .widget-message-citation (the numbered chip inside an answer)
Recommendations:
.widget-recommendations, .widget-recommendation
Composer:
.widget-composer, .widget-composer-input, .widget-composer-button
Open button:
.widget-open-button, .widget-open-dot, .widget-badge-label
Empty and error states:
.widget-empty-state, .widget-error
Lead capture:
.widget-lead-row, .widget-lead-card, .widget-lead-headline, .widget-lead-description, .widget-lead-field-label, .widget-lead-input, .widget-lead-error, .widget-lead-submit, .widget-lead-success
Note: These classes are stable and safe to target. Internal class names (not prefixed with
widget-) may change between releases.
Citation and source appearance#
The corners and hover highlights of the citation surfaces come from six CSS variables the widget works out from your brand color, surface style and corner roundness. Set one on the element you want to change and the widget uses your value instead.
| Variable | Applies to | Default |
|---|---|---|
--hh-source-radius |
Reference row and sources heading corners | 0.7× your roundness setting, 6–16px |
--hh-source-icon-radius |
The number tile on a reference row | 0.35× your roundness setting, 4–10px |
--hh-citation-radius |
The numbered chip inside an answer | 0.3× your roundness setting, 3–8px |
--hh-citation-bg |
The numbered chip's background | Your brand colour at 10%, the same tint the number tile uses |
--hh-citation-fg |
The numbered chip's figure | Your brand colour |
--hh-source-hover |
Reference row on hover and on keyboard focus | The row's own background, darkened on light surfaces and lightened on dark ones |
--hh-source-trigger-hover |
Sources heading on keyboard focus | The panel background, same rule |
--hh-citation-hover |
Numbered chip on hover and on keyboard focus | The chip's own brand tint, same rule |
window.HyperhelpWidget.init({
chatbotKey: "YOUR_WIDGET_KEY",
customCss: `
.widget-message-citation {
--hh-citation-bg: #eef2ff;
--hh-citation-fg: #3730a3;
--hh-citation-radius: 999px;
}
.widget-sources-icon {
--hh-source-icon-radius: 50%;
}
`
});
Note: Set these on the element itself, as above. Setting them on
.widget-panelhas no effect — that is where the widget puts its own values.
Hovering a reference row also shows the address of the page it links to, which is the part a visitor cannot read off the row when it shows a page title.
The Sources heading itself does not highlight on hover — only the rows beneath it do. If you would rather it did, style it yourself:
customCss: `
.widget-sources-trigger:hover {
background-color: #eef2ff;
}
`
The hover colors are worked out from the colors saved in your dashboard. If you recolour a reference row from the embed code — styleOverrides: { sourcesLink: { backgroundColor: "#102030" } } — set --hh-source-hover in the same snippet, otherwise the highlight stays matched to the dashboard colour it replaced.
To change a hover highlight by writing background-color directly instead of setting the variable, mark it !important on .widget-sources-link:hover — a reference row's resting color is applied to the element itself, so an ordinary rule cannot override it. Setting the variable needs no such thing and is the simpler route.
Callbacks#
Register callback functions to react to widget events:
window.HyperhelpWidget.init({
chatbotKey: "YOUR_WIDGET_KEY",
callbacks: {
onReady(payload) {
console.log("Widget loaded", payload);
},
onOpen(payload) {
// Track widget open in analytics
},
onClose(payload) {
// Track widget close
},
onConversationStart(payload) {
console.log("New conversation:", payload.conversationId);
},
onConversationReset(payload) {
// Visitor started a new conversation
},
onMessageSent(payload) {
console.log("Visitor asked:", payload.message);
},
onMessageDelta(payload) {
// Streaming chunk received (real-time)
},
onMessageCompleted(payload) {
console.log("Answer:", payload.answer);
console.log("Sources:", payload.sources);
},
onError(payload) {
console.error("Widget error:", payload.error);
},
onStatus(payload) {
console.log("Status:", payload.status);
},
onLeadShown(payload) {
// The lead capture form appeared
},
onLeadSubmitted(payload) {
console.log("Lead captured:", payload.lead.data);
},
onLeadDismissed(payload) {
// Visitor skipped the lead form
},
onLeadError(payload) {
console.warn("Lead rejected:", payload.lead.reason, payload.lead.invalidFields);
}
}
});
Callback payload#
Every callback receives a payload object with these common fields:
| Field | Type | Description |
|---|---|---|
event |
string |
The event name (e.g., "open", "messageSent") |
widget |
object |
Widget API instance with open(), close(), isOpen() methods |
chatbotKey |
string |
The chatbot's widget key |
visitorId |
string |
Unique visitor identifier (persisted in localStorage) |
conversationId |
string | null |
Current conversation ID (null before first message) |
pageUrl |
string |
The page URL where the widget is embedded |
Event-specific fields#
| Callback | Additional fields |
|---|---|
onReady |
ready — boolean, status — readiness status string |
onMessageSent |
message — the visitor's question |
onMessageDelta |
delta — the streaming text chunk |
onMessageCompleted |
answer — full response text, sources — array of cited sources |
onError |
error — error description |
onStatus |
status — current widget status string |
onLeadShown |
lead — see Lead capture events |
onLeadSubmitted |
lead — including the captured data |
onLeadDismissed |
lead |
onLeadError |
lead — including reason and invalidFields |
When the message callbacks fire#
The widget writes an answer onto the screen at a steady pace rather than in the bursts it arrives in, so the two message callbacks mark two different moments:
onMessageDeltafires as each chunk arrives. Use it to measure how quickly the answer comes back.onMessageCompletedfires once the answer has finished appearing to the visitor, shortly after the last chunk arrived. Use it for anything about the completed answer — logging it, forwarding it, marking the exchange done.
Lead capture events#
The four onLead* callbacks let you forward captured leads to your CRM, email tool or analytics as they happen. Each
receives a lead object alongside the common payload fields:
| Field | Type | Present on | Description |
|---|---|---|---|
timing |
string |
all | "startup" or "after_first_exchange" — when the form is configured to appear |
fields |
string[] |
all | Your form's field keys, in display order |
data |
object |
onLeadSubmitted |
The captured values, keyed by field |
submittedAt |
string | null |
onLeadSubmitted |
ISO 8601 timestamp of the saved lead |
reason |
string |
onLeadError |
"validation" (input rejected) or "request" (submission failed) |
invalidFields |
string[] |
onLeadError |
Field keys that failed validation |
Use fields — not the labels shown in the form — to map values onto your own system. Field keys are fixed once saved,
so renaming a label will not break your integration.
window.HyperhelpWidget.init({
chatbotKey: "YOUR_WIDGET_KEY",
callbacks: {
onLeadSubmitted(payload) {
fetch("https://your-crm.example.com/leads", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
...payload.lead.data,
capturedAt: payload.lead.submittedAt,
conversationId: payload.conversationId,
sourcePage: payload.pageUrl
})
});
}
}
});
Behavior worth knowing when you build on these:
onLeadShownfires each time the form appears. Withafter_first_exchangetiming a form the visitor ignored appears again in their next conversation, and that counts as a new impression.onLeadSubmittedfires only after the lead is stored, and reports the values as saved rather than what was typed. It cannot fire for a lead you will not find in your dashboard, so it is safe to trigger a CRM record or a conversion pixel from it.onLeadDismissedmeans the visitor used your skip link. A form that closes because the lead was accepted does not report a dismissal.- A visitor whose details you already hold triggers nothing at all when they resubmit, so you will not get duplicates.
- Errors in your callbacks cannot break the chat. Exceptions are caught and ignored by the widget.
Full example#
A complete embed snippet combining copy overrides, style overrides, and callbacks:
<script
src="https://app.hyperhelp.ai/widget/chat.js"
async
onload='window.HyperhelpWidget.init({
"chatbotKey": "YOUR_WIDGET_KEY",
"copy": {
"title": "Ask Acme",
"subtitle": "Answers from our docs",
"badge_label": "Help"
},
"styleOverrides": {
"openButton": { "backgroundColor": "#1e40af" },
"header": { "background": "linear-gradient(135deg, #1e40af, #3b82f6)" },
"title": { "color": "#ffffff" },
"subtitle": { "color": "#bfdbfe" }
},
"callbacks": {
"onConversationStart": function(p) {
analytics.track("widget_conversation", { page: p.pageUrl });
}
}
})'
></script>