JUST Docs
WidgetReference

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

EventDispatched onDirectionPayload
just:ai-agent:openwindowhost → widgetoptional { message, source, firstAssistantMessage, shortcut, workflow }
just:ai-agent:closewindowhost → widgetnone
cart-add-requested<just-ai-agent> elementwidget → host{ variantId, quantity }
cart-clicked<just-ai-agent> elementwidget → hostnone
cart-added<just-ai-agent> elementhost → widgetoptional { 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:open listener 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:close fired 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 message waiting on a firstAssistantMessage typewriter is dropped by a just:ai-agent:close event, 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

On this page