How the Concierge is controlled
The Concierge renders in a closed shadow DOM, so you never reach into it directly. Instead it listens for events onwindow and dispatches events back the same way.
Events are dispatched on
window, not on the <bonsai-chat-bubble> element. Attach listeners with window.addEventListener(...). A listener bound to the element itself will never fire.Events you dispatch
bonsai:open-chat
Opens the chat panel, reusing the configuration on your <bonsai-chat-bubble> tag. This is the recommended way to open the Concierge from your own UI.
This event is ignored if no
<bonsai-chat-bubble> is present on the page, since that tag is what supplies the API key and panel configuration.
bonsai:search
Tells the Concierge that a shopper ran an on-site search, so it can offer to refine the results conversationally. The Bonsai search components dispatch this for you; dispatch it yourself only if you are wiring up a search experience the Concierge does not already know about.
Set disable-search-refine on the tag to make the Concierge ignore this event entirely.
bonsai:agent-reset-search
Clears the remembered search state, so the next bonsai:search is treated as a fresh query rather than a repeat.
Events you listen for
bonsai:support-handoff
Fires when the conversation reaches a point where a human should take over. Use it to open your own support widget, route to a contact form, or record the escalation.
Requires the
handoff attribute. Without it the header button and the support cards are not rendered, and the automatic escalation does not run, so this event never fires.auto handoff fires once per message — the component records which messages have already escalated, so reloading the page or reopening the panel does not re-fire it for the same reply.
The window.BonsaiAgent global
Loading the script exposes two functions:
openChat requires a configuration object — it does not read your <bonsai-chat-bubble> tag. At minimum it needs apiKey and externalId:bonsai:open-chat instead. It reuses the configuration already on your tag, so your API key stays in one place and every entry point behaves identically.closeChat() takes no arguments and is safe to call when the panel is already closed.
The global is assigned asynchronously just after the script loads. If you call it from an inline script in the same tick, guard for it: