Nimbia Widget Integration Guide
This guide shows how to add the Nimbia widget to your web app and control it from your own code. The widget loads from one script tag.
Quick Start
Add this snippet to every page where the widget should appear, and fill in the visitor's details:
<script>
(function (w, d) {
if (w.Nimbia) return;
var q = [];
var n = { _q: q };
"identify update shutdown boot startGuide stopGuide pauseGuide resumeGuide startCall endCall sendText continueCall dismissCtaModal off".split(" ").forEach(function (m) {
n[m] = function () {
var a = [].slice.call(arguments);
return new Promise(function (res, rej) { q.push([m, a, res, rej]); });
};
});
["on", "once"].forEach(function (m) {
n[m] = function (e, h) {
q.push([m, [e, h]]);
return function () { w.Nimbia.off(e, h); };
};
});
n.ready = new Promise(function (r) { n.once("ready", r); });
w.Nimbia = n;
var s = d.createElement("script");
s.type = "module";
s.async = true;
s.src = "https://widget.nimbia.ai/assets/widget.js";
d.head.appendChild(s);
})(window, document);
Nimbia.boot({
platformId: "your-platform-id", // from the Nimbia dashboard
user: {
id: user.id,
email: user.email,
name: user.fullName,
createdAt: user.createdAt, // sign-up date, ISO 8601
locale: user.locale, // e.g. "en", "nl"
company: { id: user.companyId, name: user.companyName },
},
});
</script>
The first part puts a stub on window.Nimbia and loads the widget script without blocking the page. The stub queues every call made before the script has loaded and runs them in order once it has, so the boot() call below it works.
boot() starts the widget. It takes:
| Option | Type | Required | Description |
|---|---|---|---|
platformId |
string | Yes | Your platform ID from the Nimbia dashboard |
user |
object | No | The visitor, as in the table below. Leave it out to boot now and call identify() once you know who the visitor is |
settings |
object | No | Settings to start with, e.g. { visible: false } |
boot() returns a promise that resolves once the visitor's session has started, or at once without user. It also still accepts the older snake_case config (platform_id, user_id, email, full_name, ...), and initialize() is a deprecated name for boot(). window.Hyper is a deprecated alias for window.Nimbia.
With npm (coming soon)
Coming soon: the
@nimbia/jspackage isn't published yet. Until then, use the script snippet above.
Once it's published, an app with a bundler can install the package instead of pasting the snippet:
npm install @nimbia/js
import { loadNimbia } from "@nimbia/js";
const nimbia = await loadNimbia({
platformId: "your-platform-id",
user: { id: user.id, email: user.email, name: user.fullName },
});
await nimbia.startGuide("your-guide-id");
loadNimbia() adds the same script to the page once and returns the same object as window.Nimbia, with TypeScript types. With user, it resolves once the session has started.
Who the visitor is
boot({ user }) and identify(user) take the visitor's details:
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | The visitor's email address |
id |
string | No | Your ID for the visitor. Defaults to email |
name |
string | No | Full name |
createdAt |
string | No | When the visitor signed up, as an ISO 8601 date |
locale |
string | No | Preferred language code, e.g. "en" |
company |
object | No | { id, name, meta? } |
meta |
object | No | Anything else to store on the visitor's record |
Logging out
When the visitor logs out, call shutdown(). It ends the session, clears what the widget stored, and removes it from the page, so the next visitor doesn't inherit the session.
async function handleLogout() {
await Nimbia.shutdown("logout");
// ... the rest of your logout logic
}
Pass a reason. A shutdown without one that interrupts a running guide is reported on our side as a possible integration problem.
When the next visitor logs in, call boot() again with their details. If a visitor logs in while the widget runs for an anonymous one, call identify() instead: it switches the running widget to the new visitor, which ends a running call.
JavaScript API
window.Nimbia has the functions and events below. The coming loadNimbia() returns the same object.
How the API is named
- Anything
getState()reads back is a setting, andupdate()changes it. Anything that happens once, such as starting a guide or hanging up, is a verb that returns a promise.pauseGuide()is a verb, because whether a guide is paused belongs to the running guide and ends with it. update()changes the keys you name and leaves the rest alone. The type isNimbiaUpdateOptions; the type forboot()isNimbiaBootOptions.- Names are positive:
visible, nothidden;microphoneEnabled, notmuted. - There are no toggles.
update({ microphoneEnabled: false })leaves the microphone off whatever it was before, so two calls can't undo each other. identify()says who the visitor is and starts a new session for them.update()says how the widget behaves and leaves the session alone.- A call made before the script has loaded is queued and runs once it has. An action called before the session has started waits for it.
- The prompt a first-time visitor sees, "Ready for a 2-minute walkthrough?", is the CTA modal.
dismissCtaModal()closes it andgetState().ctaModalVisiblesays whether it shows. on()returns a function that removes the handler.
Nimbia.update({ visible: !onLoginPage, inputMode: "chat" }); // settings
await Nimbia.startGuide("guide-id"); // an action; fine before the widget is ready
const stop = Nimbia.on("guideEnded", onGuideEnded);
stop();
Wait for the widget
The widget is ready once Nimbia has started the visitor's session, a moment after boot() with a user or after identify(). Actions wait for it themselves. To wait in your own code:
await Nimbia.ready; // a promise
Nimbia.on("ready", () => {}); // an event; runs at once if the widget is already ready
Nimbia.getState().ready; // a boolean
getState() exists only once the script has loaded. Before that, use Nimbia.ready or the events.
Settings
update(options) changes the settings you name and leaves the rest alone. getState() returns their current values.
| Setting | Values | What it does |
|---|---|---|
visible |
true, false |
Shows or hides the whole widget. A running call keeps going while it's hidden. |
minimized |
true, false |
Minimizes the onboarding checklist. |
chatOpen |
true, false |
Opens the chat panel during a call. A running guide pauses while it's open. |
inputMode |
"voice", "chat" |
Whether the visitor talks to Nimbia or types. |
microphoneEnabled |
true, false |
The visitor's microphone during a call. |
speakerEnabled |
true, false |
Nimbia's voice during a call. |
The widget keeps a setting it can't apply yet and applies it as soon as it can. speakerEnabled: false with no call running turns Nimbia's voice off once the next call starts, and getState() shows the kept value until then. inputMode: "chat" before a call makes the next call, or the next guide, start in chat. Settings made before the script has loaded or before the session has started apply once it has.
chatOpen, inputMode, microphoneEnabled and speakerEnabled belong to a call and reset when it ends. visible and minimized stay, also across identify().
update() logs a warning and skips a key it doesn't know or a value of the wrong type.
// Keep the widget off the login form
Nimbia.update({ visible: location.pathname !== "/login" });
Actions
Each action returns a promise. It resolves once the action has started and rejects with a NimbiaError if it can't. An action called before the widget is ready waits for it.
| Action | Resolves when |
|---|---|
startGuide(guideId) |
The guide has started |
stopGuide() |
The guide has ended (at once if none is running) |
pauseGuide() |
The guide is paused. An open chat panel already pauses it, so this resolves at once then |
resumeGuide() |
The guide runs again |
startCall() |
Nimbia is listening, in voice or chat as inputMode says |
endCall() |
The call has ended (at once if there is none) |
sendText(text) |
The message is sent, as if the visitor typed it |
continueCall() |
The answer to Nimbia's "Still here?" question is sent |
dismissCtaModal() |
The CTA modal is closed and the checklist minimized, as its Skip button does |
boot(options) |
The visitor's session has started; at once without user |
identify(user) |
The new visitor's session has started |
shutdown(reason?) |
The widget is gone from the page |
Start a guide
startGuide(guideId) starts a guide the way the Start button in the widget's checklist does. A visitor who hasn't been asked about the microphone yet is asked first whether they want to talk or type. With inputMode: "chat" the guide starts in chat without the question.
try {
await Nimbia.startGuide("your-guide-id");
} catch (error) {
if (error.code === "timeout") showHelpLink();
}
If you call startGuide() several times before the widget is ready, only the latest call runs, and the earlier ones reject with superseded. You find a guide's ID in the Nimbia dashboard.
startMilestone(guideId) is a deprecated alias. It skips the microphone question, as it always has.
Errors
code |
Meaning |
|---|---|
init_failed |
The widget couldn't start a session for the visitor. It shuts itself down, so you can call boot() again |
call_failed |
A call couldn't start, or it dropped |
timeout |
The action didn't start in time (2 minutes for a guide, 1 minute for a call, 15 seconds for the rest) |
superseded |
A newer startGuide() call replaced this one before the widget was ready |
no_call |
The action needs a running call |
no_guide |
The action needs a running guide |
not_configured |
boot() had no platformId, or identify() ran before boot() named the platform |
shut_down |
shutdown() ran before the session started |
Check error.code or error.name === "NimbiaError". instanceof NimbiaError fails when the page and the widget script each have their own copy of the class.
Events
on(event, handler) adds a handler and returns a function that removes it. once() does the same for a single run, and off(event, handler) removes a handler too.
| Event | Sent when | Payload |
|---|---|---|
ready |
The session has started | none |
change |
Anything in getState() changed |
{ state, changed }, where changed lists the keys |
guideStarted |
A guide has started | { guideId } |
guideEnded |
A guide has ended | { guideId, reason }, with reason "completed", "stopped" or "error" |
callStarted |
A call has connected and Nimbia is listening | none |
callEnded |
A call has ended | none |
error |
Starting the session or a call failed | a NimbiaError |
const stop = Nimbia.on("guideEnded", ({ guideId, reason }) => {
if (reason === "completed") analytics.track("guide_completed", { guideId });
});
// Later:
stop();
State
getState() returns the settings above plus:
| Field | Values |
|---|---|
ready |
true once the session has started |
ctaModalVisible |
true while the CTA modal shows: the prompt a first-time visitor sees, "Ready for a 2-minute walkthrough?" |
call |
"idle", "connecting" or "connected". It is "connected" once Nimbia can hear the visitor, not when the line opens |
guide |
null, or { id, step, paused }, where step is what the current step is doing ("loading", "audio_playing", "executing_dom_action", "done", "handover" or null) and paused is true while the guide is paused, by pauseGuide() or by an open chat panel |
user |
null, or { id, email, name } |
Nimbia.on("change", ({ state, changed }) => {
if (changed.includes("call")) {
document.body.classList.toggle("in-call", state.call === "connected");
}
});
TypeScript
Coming soon: the types ship with the
@nimbia/jspackage, which isn't published yet. Until then, use the script snippet; the tables on this page list every function, setting and event.
Once it's published, @nimbia/js exports the types: NimbiaUpdateOptions, NimbiaState, NimbiaEvents and its payloads, NimbiaIdentity, NimbiaError and NimbiaErrorCode, and the Nimbia interface. With the snippet, install the package for its types and add this once, which types window.Nimbia:
import type {} from "@nimbia/js/global";
Browser Compatibility
Safari with Intelligent Tracking Prevention (ITP)
When your application (that includes the Nimbia widget) is embedded as an iframe on a different domain, Safari users with ITP enabled will experience session persistence issues. Sessions will not persist across page navigations, creating a new session on each page load.
Recommended solutions: - Use a Single Page Application (SPA) - no page navigations means no session loss - Avoid embedding your application in cross-domain iframes - Host your application on the same domain as the parent frame - See Safari ITP Limitations for detailed information
Troubleshooting
When troubleshooting integration issues, we recommend examining the browser console for any error messages that might provide insight into the problem. It's also important to confirm that your configuration includes all required fields with valid values. Although most values are optional, the platform UUID is the most important one to get right. Additionally, verify that the widget script URL is properly accessible from your domain and that both your platform identifier and user identifier have been correctly configured and are valid within the Nimbia system.
Support
For additional support or questions, please contact our support team or refer to our API documentation.