Events
The full event contract of the widget, covering window events, element events, payloads and ordering rules.
The widget talks to the host page through DOM events, in both directions. Window events open and close the panel; element events delegate the cart to the host on headless storefronts.
The contract at a glance
| Event | Dispatched on | Direction | Payload |
|---|---|---|---|
just:ai-agent:open | window | host → widget | optional { message, source, firstAssistantMessage, shortcut, workflow } |
just:ai-agent:close | window | host → widget | none |
cart-add-requested | <just-ai-agent> element | widget → host | { variantId, quantity } |
cart-clicked | <just-ai-agent> element | widget → host | none |
cart-added | <just-ai-agent> element | host → widget | optional { variantId, quantity } |
Two attributes travel with the element events and complete the contract:
cart-count (shows the header bag and drives its badge) and cart-id (enables attribution and the
confirmation card). Their parsing rules live in the
attributes reference.
just:ai-agent:open: the detail payload
Dispatch on window. Every field is optional: a bare
new Event('just:ai-agent:open') just opens the panel.
window.dispatchEvent(
new CustomEvent('just:ai-agent:open', {
detail: { message: 'Help me choose a gift' },
}),
);message
Sent as the shopper's first message (shown in a bubble) as soon as the session is ready. If the event fires before the session exists, the message is queued and sent when the session lands.
source
Analytics origin of the open, stamped on the just_ai_widget_opened event. Defaults
to trigger_block when omitted. Recognized values:
toggle · auto_open · trigger_bar · trigger_bar_reopen ·
trigger_bar_shortcut · trigger_block · search_bar · product_block
Any other string is passed through to analytics unchanged, but the structured
context sent with the message falls back to trigger_block for unrecognized
values.
shortcut
true, on a product page, sends message as a PDP shortcut click,
exactly like a shortcut on the widget's own trigger bar: the page's product is
focused with a bookmark card, and the turn is flagged as a shortcut. This is
the field the product block's
chips dispatch.
Ignored off a product page, without a message, or combined with workflow.
firstAssistantMessage
A synthetic assistant bubble, rendered locally with the typewriter effect. Display only: it is never sent to the backend as conversation history. Silently dropped when the chat already has messages, to avoid mid-conversation discontinuity.
workflow
true opens the widget and launches the shop's live guided flow,
Expert Mode. The event carries no
workflow id on purpose: which flow is live is a dashboard setting, so
switching flows needs no theme edit.
workflow wins over message: the text sent to the backend is a fixed
internal launch instruction, and message becomes the label displayed in the
shopper's bubble. Without one, a trigger block falls back to
data-workflow-first-message, then to the generic label ("Let's go!"). When the
shop has no live flow, the open falls back to a normal conversation, silently.
Ordering and robustness
- Nothing is lost before load. The
just:ai-agent:openlistener is registered at module load, before the app mounts: an event dispatched while the bundle is still initializing is buffered and replayed once the widget is ready. - The most recent instruction wins. Only the latest buffered open is
kept, and a
just:ai-agent:closefired before mount is itself a no-op (the widget mounts closed) but discards any buffered open. - A queued auto-message is cancelled by the close event only. A
messagewaiting on afirstAssistantMessagetypewriter is dropped by ajust:ai-agent:closeevent, so a host that closes the panel programmatically never gets a late auto-send. The shopper's own close button or a conversation reset does not clear the queue: the send still fires when the typewriter completes. - Bubble order with
firstAssistantMessage+message: the synthetic assistant bubble renders first; when its typewriter completes, the shopper bubble is sent; the real answer lands after it.
just:ai-agent:close
Dispatch on window; no detail. Closes the panel back to the trigger bar and
never reloads the host page. Typical use on headless: close the widget when
your cart drawer opens, so the panel never covers it.
window.dispatchEvent(new Event('just:ai-agent:close'));Element cart events (headless)
On headless storefronts the host owns the cart; these three events delegate
it. They are dispatched on the <just-ai-agent> element, and the widget's
events set bubbles and composed, so a listener on document works too.
The full wiring guide is
headless installation.
Two elements on the page?
Once a product block is installed, the page holds two <just-ai-agent>
elements. Cart wiring belongs to the standalone one: select it with
document.querySelector('just-ai-agent:not([mode="block"])').
cart-add-requested: widget → host
The shopper adds a product in the chat; the widget asks your storefront to perform the write instead of touching the cart itself.
detail: {
variantId: string;
quantity: number;
}variantId is a Shopify ProductVariant GID: use it directly as the
Storefront API merchandiseId. Perform the mutation with your own cart
client, then update cart-count, keep cart-id published, and report the
add with cart-added.
cart-clicked: widget → host
The shopper taps a cart entry point (the bag in the widget header, the confirmation
card's "View cart" CTA). No detail. Open your own cart UI, and dispatch
just:ai-agent:close in the same handler, so the panel does not cover it:
widget.addEventListener('cart-clicked', () => {
openMyCartDrawer();
window.dispatchEvent(new Event('just:ai-agent:close'));
});cart-added: host → widget
Your report that an add-to-cart succeeded. Dispatch it after your own cart write succeeds, never optimistically, and for every add on the storefront (product page, quick-add, cart drawer), not only the ones the widget requested: that is what makes the dashboard's store-baseline add-to-cart column meaningful, and an add the widget did not ask for never produces a chat card.
detail?: { variantId?: string; quantity?: number }Both fields are optional and the add is counted regardless; a missing or
invalid quantity counts as 1. When the report correlates with an add the
widget requested and cart-id is published, the widget reads your cart back
and renders the confirmation card from its real lines. See
Analytics for what each signal unlocks.
Going further
- Trigger button: open the widget with a plain HTML attribute, no JS.
- Expert Mode: the guided flow
behind
workflow: true. - Headless installation: the complete cart bridge, ready to paste.
- Analytics: the metrics these events feed.