/Docs
Start free

Widget JavaScript API

Everything on window.SellioWidget after the embed script loads.

Overview

Install the embed with a framework guide, then call the API from your page. The public methods are open, close, toggle, setVisitor, sendMessage, requestFeedback, hide, show, on, consent, reset, and destroy, plus the isReady property.

✦
Prefer identifying signed-in users with identity verification when a claimed email must be trusted.

sellioSettings

Set this before embed.js runs. The loader reads widgetKey from it.

settings.js
window.sellioSettings = {
  widgetKey: "your-widget-key",
  // optional: baseUrl: "https://sellio.chat",
};
widgetKey

string

Public workspace key from Settings → Install. Safe to commit.

baseUrl

string?

Override the Sellio origin when loading the embed (advanced).

nonce

string?

CSP nonce for the widget's styles on older browsers. Usually not needed: the nonce on the embed script tag is picked up on its own.

deferIdentity

boolean?

Consent mode. Nothing is stored in the browser and the visitor is not registered until they open the chat or you call SellioWidget.consent(). The same as a data-defer-identity attribute on the embed script tag. See Consent mode below.

trackQueryParams

string[]?

Query parameters to keep in the page URLs and referrer the widget reports, besides utm_*. Everything else in the query, and the #fragment, is dropped before it leaves the page, so reset tokens and sign-in codes never reach your inbox.

ℹ
Those are the whole of what the page can set. Colour, position, launcher size, greeting, pre-chat form, auto-open, which pages and devices show the widget, and the visitor rate limit are all set in the dashboard and delivered to the widget when it loads, so a change there reaches every page without a deploy. See Settings and The chatbox.

Content Security Policy

The widget renders in a shadow root on its own element under <html>, so the CSS of your page does not reach it and a framework that swaps <body> on navigation (Turbo, htmx, Astro, Swup) leaves it in place. It uses no innerHTML and, in current browsers, no inline <style>, so it works under Trusted Types and a strict style-src. Your policy needs to allow:

csp.txt
script-src  https://sellio.chat
connect-src https://sellio.chat
frame-src   https://sellio.chat

With a nonce-based script-src, put the nonce on both the settings script and the embed script tag. The chat itself runs in the frame, under our policy, not yours.

On browsers without constructable stylesheets (Safari before 16.4) the styles fall back to a <style> element that carries the nonce of the embed script tag, or sellioSettings.nonce when you set one.

Command queue

The snippet is async. Hosts can queue calls before the API exists:

queue.js
window.SellioWidget = window.SellioWidget || [];
window.SellioWidget.push(["open"]);
window.SellioWidget.push(["setVisitor", { id: "u_1", email: "ada@example.com" }]);

When embed.js finishes, queued [method, …args] entries are replayed on the real API.

Methods

open()

void

Open the chat panel. Called before the widget has started, it waits up to 30 seconds. When the chat cannot open (the widget failed to start, is blocked, or is hidden on this page by your visibility rules) the call is dropped and an error event says why, so a "Chat with us" button can fall back to email. On phones the chat is full screen: the page behind stops scrolling and the Back button closes the chat.

close()

void

Close the chat panel.

toggle()

void

Toggle open / closed.

setVisitor(data)

void

Attach userId (or id), name, email, phone, and optional userHash. Queued until the iframe is ready. A userId different from the last one on this browser is another person: the widget resets first, so they never see the previous user's conversations.

sendMessage(message)

void

Send a visitor message as if they typed it. Takes a non-empty string, trimmed and cut at 4,000 characters; anything else is ignored with a console warning.

requestFeedback()

void

Ask the visitor to rate the open conversation: the feedback sheet slides over the thread. Does nothing until there is a conversation.

hide()

void

Hide the launcher, any pop-up and the chat without removing the widget. An open chat is closed first, the chat stays loaded and keeps counting unread replies, and open() while hidden reports OPEN_FAILED with reason HIDDEN.

show()

void

Bring the widget back after hide().

isReady

boolean

True once the launcher is on the page, the same moment the ready event fires.

on(event, callback)

() => void

Subscribe to widget events. Returns an unsubscribe function; destroy() removes any subscription still attached.

consent()

void

Consent mode only: the visitor accepted, so the widget may use storage and register them now rather than on the first open. Does nothing otherwise.

reset()

void

Call on sign out. Forgets this browser's visitor (stored id, token and anonymous id), ends the chat session and starts again as a new anonymous visitor, so the next person on a shared computer does not see the previous user's conversations.

destroy(options?)

void

Tear down the launcher, iframe, window listeners, the history patch, timers and your on() subscriptions, and give back the page title and scroll. An open chat is closed first, so you still receive close. Safe to re-embed afterwards. The visitor is kept unless you pass { forgetVisitor: true }.

Events

on listens for sellio:<event> CustomEvents on window. Detail is passed to your callback.

ready

event

Embed finished booting: the launcher is on the page. A subscriber added later still receives it once.

open

event

Chat panel opened.

close

event

Chat panel closed.

newMessage

event

A new message arrived in the widget.

unreadChange

event

Unread count changed, detail { count }. Fires only on a real change, including the reset to 0 when the chat opens, and always matches the launcher badge.

error

event

Something the page asked for cannot happen, detail { code, reason?, status?, message? }. Codes below.

INIT_FAILED

code

The widget could not start: its configuration did not load after two retries with a 10 second timeout each, or the answer was an error. status is set when there was an HTTP answer, and reason when the server refused and said why: ORIGIN_NOT_ALLOWED (this domain is not in the widget's allowed domains, see Settings > Install), WIDGET_DISABLED, INVALID_WIDGET (no widget has this key) or RATE_LIMITED. reason is CSP when your page's Content-Security-Policy blocks the widget; the console names the directive and the origin to allow. The browser console says the same in plain words.

BLOCKED

code

The widget is switched off for this visitor (for example by country rules).

HIDDEN_BY_RULES

code

open() on a page where your visibility rules hide the widget. The call is dropped, not replayed on a later page.

OPEN_FAILED

code

open() could not be honoured. reason is INIT_FAILED, BLOCKED, HIDDEN (you called hide()), CSP (your Content-Security-Policy blocks the chat frame), TIMEOUT (still starting after 30 seconds), RENDER_FAILED, or FRAME_TIMEOUT (the chat frame did not load within 12 seconds; the panel then says so and offers Retry, and the event fires once per attempt).

Examples

example.js
const off = SellioWidget.on("unreadChange", (detail) => {
  console.log("unread", detail);
});

SellioWidget.open();

// Fall back to email when the chat cannot open on this page.
SellioWidget.on("error", (detail) => {
  if (detail.code === "OPEN_FAILED" || detail.code === "HIDDEN_BY_RULES") {
    window.location.href = "mailto:support@example.com";
  }
});

// On sign in: identify the user (a different user resets on its own).
SellioWidget.setVisitor({ userId: "u_1", email: "ada@example.com" });

// On sign out: forget the visitor and start a fresh anonymous chat.
SellioWidget.reset();

// On route teardown (the visitor is kept):
off();
SellioWidget.destroy();

Updated . Something wrong or missing? Tell us.