Skip to content

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/js package 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, and update() 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 is NimbiaUpdateOptions; the type for boot() is NimbiaBootOptions.
  • Names are positive: visible, not hidden; microphoneEnabled, not muted.
  • 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 and getState().ctaModalVisible says 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/js package, 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.