JavaScript

Widget JavaScript SDK

Once the chat widget loads, it adds window.SellWhisperWidget to your page. Call its methods to control the widget from your own code, and listen for its events to react to the chat.

Before you start

Add the embed code from your widget first (see the deployment guides). The script loads asynchronously, so wait for window.SellWhisperWidget before calling it:

function whenWidgetReady(callback) {
  if (window.SellWhisperWidget) return callback(window.SellWhisperWidget);
  const timer = setInterval(() => {
    if (window.SellWhisperWidget) {
      clearInterval(timer);
      callback(window.SellWhisperWidget);
    }
  }, 100);
}

whenWidgetReady((widget) => widget.open());

Integrations written before the rename can keep using window.SolveoWidget. It points to the same methods and logs a one-time deprecation warning.

Methods

open

open(): void

Opens the chat window. Use it to open the widget from your own button or link.

document.querySelector("#help-button").addEventListener("click", () => {
  window.SellWhisperWidget.open();
});

close

close(): void

Closes the chat window and its conversation history panel. An inline widget always stays open.

window.SellWhisperWidget.close();

resetChat

resetChat(): void

Clears the current conversation so the next message starts a new one. The old conversation stays in the visitor's history. Call it when a user logs out.

function onLogout() {
  window.SellWhisperWidget.resetChat();
}

openConversation

openConversation(conversationId: string): Promise<void>

Opens the widget on an existing conversation and loads its messages, so you can deep-link a visitor back into it. If the conversation cannot be loaded, the widget stays as it was and an error is logged to the console.

const id = new URLSearchParams(location.search).get("conversation");
if (id) await window.SellWhisperWidget.openConversation(id);

setOptions

setOptions(options: { appearance?, behavior?, branding?, advanced? }): void

Changes the widget's settings for this page view. Each group takes the same keys as the widget builder (for example behavior.suggested_messages, branding.title, appearance.primary_color). To greet a logged-in visitor personally, pass behavior.initial_messages: a list of messages the widget shows before the conversation starts, in place of your greeting. Keys you leave out keep their current value. Nothing is saved to your widget.

window.SellWhisperWidget.setOptions({
  behavior: {
    initial_messages: [
      "Hi Sara, welcome back!",
      "Your order #1042 ships tomorrow. Ask me anything about it.",
    ],
    suggested_messages: ["Track my order", "Start a return"],
  },
  branding: { title: "Order help" },
});

resetOptions

resetOptions(): void

Undoes every setOptions call and restores the settings the widget loaded with.

window.SellWhisperWidget.resetOptions();

sendMessage

sendMessage(text: string): void

Sends a message as if the visitor had typed it. Pair it with open() so the visitor sees the reply.

window.SellWhisperWidget.open();
window.SellWhisperWidget.sendMessage("Where is my order?");

onAddToCart

onAddToCart(handler: (request) => Promise<{ requestId, ok, message? }>): void

Registers your add-to-cart function for custom storefronts. The widget calls it when the visitor adds a recommended product to the cart; resolve with ok: true once the item is in the cart.

window.SellWhisperWidget.onAddToCart(async (request) => {
  await myCart.add({ url: request.url, quantity: request.quantity });
  return { requestId: request.requestId, ok: true };
});

on

on(event: string, handler: (payload) => void): () => void

Listens for one of the widget events below. Returns a function that stops listening. A handler that throws is logged and never breaks the widget.

const stop = window.SellWhisperWidget.on("assistant-message", (message) => {
  console.log("Agent said:", message.content);
});
// Later: stop();

off

off(event: string, handler: (payload) => void): void

Stops calling a handler you passed to on() for that event.

function logReply(message) {
  console.log(message.content);
}
window.SellWhisperWidget.on("assistant-message", logReply);
window.SellWhisperWidget.off("assistant-message", logReply);

Events

Listen with on(). Each handler receives the payload shown under the event name.

user-message

{ content: string, conversationId: string | null, timestamp: string }

The visitor sent a message: typed, tapped or sent with sendMessage(). conversationId is null until the first message starts a conversation.

window.SellWhisperWidget.on("user-message", (message) => {
  analytics.track("Chat message sent", { length: message.content.length });
});

assistant-message

{ content: string, conversationId: string | null, messageId: string | null, sender: "ai" | "human", senderName: string | null, timestamp: string }

A reply arrived. sender is "ai" for your agent and "human" for a teammate answering a live chat (senderName is their name). Fires once per reply, never for messages reloaded from history.

window.SellWhisperWidget.on("assistant-message", (message) => {
  if (message.sender === "human") showBadge(message.senderName + " replied");
});

tool-call

{ name: string, arguments: object, conversationId: string | null }

Your agent ran an action while answering, for example shopify.add_to_cart. name is the action's id and arguments are the values the agent gave it. Fires before the reply's assistant-message.

window.SellWhisperWidget.on("tool-call", (call) => {
  console.log("Agent is running", call.name, call.arguments);
});

tool-result

{ name: string, success: boolean, conversationId: string | null }

The outcome of the action in the matching tool-call. success is false when the action failed or is still waiting for the visitor to confirm or fill in details. The action's raw result stays on our servers.

window.SellWhisperWidget.on("tool-result", (result) => {
  if (result.name === "shopify.add_to_cart" && result.success) refreshCartIcon();
});

conversation-status

{ status: "open" | "escalated" | "paused" | "ended", conversationId: string | null }

The conversation changed status: open (your agent is answering), escalated (handed to your team), paused (your agent is paused for this conversation, so messages are kept but not answered) or ended (a teammate resolved the live chat). Fires only when the status changes.

window.SellWhisperWidget.on("conversation-status", ({ status }) => {
  document.body.dataset.chatStatus = status;
  if (status === "escalated") showNotice("A teammate will join shortly.");
});

message-feedback

{ messageId: string, feedback: "THUMBS_UP" | "THUMBS_DOWN", conversationId: string | null }

The end user rated one of your agent's answers with thumbs up or down, and it was saved. They can change their mind, so the same answer can fire again with the other rating.

window.SellWhisperWidget.on("message-feedback", ({ feedback }) => {
  analytics.track("Chat answer rated", { feedback });
});

conversation-deleted

{ conversationId: string }

The end user deleted their conversation from the widget. It is gone from your chat logs too.

window.SellWhisperWidget.on("conversation-deleted", ({ conversationId }) => {
  console.log("Conversation deleted", conversationId);
});

Every event is also dispatched on window as a sellwhisper: event with the payload in event.detail, so you can listen before the widget has loaded:

window.addEventListener("sellwhisper:conversation-status", (event) => {
  console.log(event.detail.status);
});

Listeners written before the rename keep working: each event also fires as solveo: (for example solveo:user-message), and window.SolveoWidget.on is the same method.