The global object
When the script loads it creates window.GlimpzeWidget. All methods below live on that object. The script loads asynchronously, but you never have to wait for it: every command is queued until the widget has booted and resolves to a result either way, and await GlimpzeWidget.ready() tells you when it is live. See Results, ready() and version.
open(config?)
Opens the widget. Without an action it opens to whatever the widget's Opening behavior is set to in the dashboard: the welcome screen, straight to chat, or straight to a call (Settings > widget editor > Content > Opening behavior). Passing action overrides that for a single call. Pass prefillFields (or visitorInfo / customData) to pre-populate the visitor.
GlimpzeWidget.open();
GlimpzeWidget.open({ prefillFields: { email: 'jane@acme.com' } });
// Deep-link straight into a call:
GlimpzeWidget.open({ action: 'call' });
// Skip the camera/mic device-check screen and connect right away (pair with
// prefillFields so the visitor's required details aren't skipped):
GlimpzeWidget.open({
action: 'call',
callPreferences: { media: 'video' },
prefillFields: { name: 'Jane', email: 'jane@acme.com' },
skipSetup: true,
});
// Open as a popover growing out of an element instead of the corner:
GlimpzeWidget.open({ anchor: document.querySelector('#pricing-help') });
GlimpzeWidget.open({ anchor: '#pricing-help' });| Option | Type | Description |
|---|---|---|
prefillFields | Record<string, string | number | boolean> | Field values keyed by field name. See Prefilling visitor details. |
visitorInfo | { name?: string; email?: string } | Lower-level identity prefill. |
customData | Record<string, unknown> | Lower-level custom-data prefill / visitor metadata. |
action | 'call' | 'chat' | 'home' | Where to land once the widget opens. 'call' deep-links into a call, skipping the home screen: the visitor lands on the camera/mic device check before connecting (unless skipSetup). 'chat' opens the conversation directly. 'home' shows the welcome screen. Omit to follow the widget's configured Opening behavior. |
callPreferences | { media: 'audio' | 'video' } | Call media when action: 'call'. Defaults to video. |
skipSetup | boolean | With action: 'call': false (default) shows the camera/mic device-check screen first; true skips that screen and connects right away. The device check still runs (invisibly) to acquire the mic/camera, so the screen is only skipped when the visitor's required fields are already satisfied; pair with prefillFields, or they're still asked for their details first. |
anchor | HTMLElement | string | false | Anchored placement: open the panel as a popover growing out of this element (or CSS selector) instead of the corner. An explicit element wins over the dashboard's placement mode; false forces the corner. Desktop only; mobile always opens fullscreen. Side, gap, and drag behaviour come from Settings > widget editor > Triggers > Opening placement. |
skipSetup still needs a user gesture the first time. Skipping the device-check screen does not remove the browser's camera/mic permission prompt. Trigger open({ action: 'call', skipSetup: true }) from a click (not on page load) so the browser allows the prompt. Visitors who already granted access on your site connect silently; if access is blocked, the in-call “Allow access” banner lets them grant it and retry.getStatus().agents.free first and fall back to your booking page if you'd rather not queue them.open() resolves to what happened. { outcome: 'opened' }, { outcome: 'mid_session' } (the visitor is already in a chat or call, so the panel came back where it was), or { outcome: 'refused', reason: 'unavailable' | 'calls_disabled' }. It never throws, so code that ignores the value keeps working. See Results, ready() and version.close()
Closes the widget. Resolves to { closed: boolean }; true when the panel was showing, false when there was nothing to close.
const { closed } = await GlimpzeWidget.close();configure(config)
Sets defaults reused by every later open() / proactiveStart(). Accepts the same keys as open() (prefillFields, visitorInfo, customData). Resolves to { supportedKeys, unsupportedKeys }; unsupported keys are ignored with a console warning. Identity you configure is stored on the visitor record right away; see identify().
const result = await GlimpzeWidget.configure({
prefillFields: { name: 'Jane', email: 'jane@acme.com' },
});
// result.supportedKeys -> ['visitorInfo']getFields()
Returns the widget's enabled form fields, in display order, so you can discover what's available to prefill. Internal columns are not exposed.
GlimpzeWidget.getFields();
// [
// { name: 'name', label: 'Name', type: 'text', required: true },
// { name: 'email', label: 'Email', type: 'email', required: true },
// { name: 'plan', label: 'Plan', type: 'single_select', required: false,
// options: ['Free', 'Pro', 'Enterprise'] },
// ]| Field | Type | Description |
|---|---|---|
name | string | The field name: the key you pass in prefillFields. |
label | string | Human label shown in the form. |
type | 'text' | 'email' | 'tel' | 'textarea' | 'single_select' | Input type. |
required | boolean | Whether the visitor must fill it. |
options | string[] | Present only for single_select. |
getStatus()
Resolves with the widget's current status. Capability tokens (conversation secrets, room URLs) are redacted from context.
const { state, isAvailable, agents, nextAvailableAt, context } = await GlimpzeWidget.getStatus();
// state: 'idle' | 'preInteractionChoice' | 'inCall' | ... (see Widget states)
// isAvailable: boolean — the team is REACHABLE: at least one agent is online.
// It stays true even when every online agent is already on a call (the visitor
// would queue). For "can a human take a call THIS INSTANT", use agents.free.
// agents: { online, free, busy } — live agent counts:
// online = agents present/working, free = present and NOT in a call,
// busy = present but currently in a call.
// nextAvailableAt: string | null — ISO time an agent is next expected free
// (from calendars), when nobody is free right now.
// All of these update live — see the AVAILABILITY_CHANGE event.identify(fields)
Tells Glimpze who this visitor is and stores it on the visitor record right away: not at their first message. Accepts the same keys as configure() (prefillFields, visitorInfo, customData) and resolves to whether the identity reached the server.
const result = await GlimpzeWidget.identify({
prefillFields: { name: 'Jane', email: 'jane@acme.com', phone: '+1 555 0100' },
});
// { persisted: true }
// or { persisted: false, reason: 'booting' | 'consent' | 'empty' | 'conflict' | 'failed' }reason | Meaning |
|---|---|
booting | Called before the widget finished booting. Pre-boot calls are queued and replayed. |
consent | The visitor has neither consented (on a consent-gated widget) nor opened the widget. The same identity is stored automatically the moment either happens. |
empty | Nothing to store. |
conflict | The email already belongs to another visitor of this project. |
failed | A transient error; safe to retry. |
open({ prefillFields }) and configure({ prefillFields }) store identity the same way. Calling identify() on every render is fine; nothing is sent unless the identity changed.
Results, ready() and version
Every command resolves to a result. open, close, configure, identify, setConsent, proactiveStart, proactiveOpen, proactiveNotify and bypassFormGate all return a promise. After boot it resolves on the next tick; before boot the call is queued and resolves when the widget replays it.
| Command | Resolves to |
|---|---|
open(), proactiveStart(), proactiveOpen() | { outcome: 'opened' | 'mid_session' | 'refused', reason? } |
close() | { closed: boolean } |
configure() | { supportedKeys, unsupportedKeys } |
identify() | { persisted: boolean, reason? } |
setConsent() | { accepted: boolean } |
proactiveNotify() | { shown: true } |
bypassFormGate() | boolean |
ready()
Resolves once the widget has booted and every queued call has been replayed, with the same snapshot getStatus() returns. Safe to await at any point on the page. The glimpze:ready window event (GlimpzeWidget.events.READY) fires once at the same moment; a late listener misses it, so use ready() when you are not sure you subscribed in time.
const { state, isAvailable, version } = await GlimpzeWidget.ready();
// Or listen for the one-shot broadcast (fires once, after boot):
window.addEventListener(GlimpzeWidget.events?.READY ?? 'glimpze:ready', (e) => {
console.log('Glimpze ready', e.detail.version);
});version
The bundle's build stamp, <date>.<sha>. Include it in support requests; it is also on getStatus() and in the glimpze:ready detail. It is undefined until the widget boots.
GlimpzeWidget.version; // '2026-09-06.ce33568' — include it in support requests
(await GlimpzeWidget.getStatus()).version; // same valueon(eventName, handler) / off(eventName, handler)
Subscribe / unsubscribe to widget events. on returns an unsubscribe function. See Events & widget states for the full event list.
const unsubscribe = GlimpzeWidget.on(
GlimpzeWidget.events.AVAILABILITY_CHANGE,
({ isAvailable }) => toggleBanner(isAvailable),
);
// later: unsubscribe();proactiveStart(config?) / proactiveOpen(conversationId, secret, config?)
Advanced entry points for proactive outreach flows. Both accept the same prefill keys as open(). proactiveStart begins a fresh proactive conversation; proactiveOpen re-opens an existing one you already hold the capability for. Most integrations only need proactiveNotify for nudges. Both resolve to the same { outcome } shape as open().
proactiveNotify(payload)
Shows a proactive nudge above the launcher. Up to 3 nudges stack with a single clear-all control.
GlimpzeWidget.proactiveNotify({
id: 'pricing-30s',
message: 'Questions about pricing? We can help.',
ctaText: 'Open chat',
openBehavior: 'start_chat', // 'start_chat' | 'open_widget' | 'open_existing_conversation'
});