Skip to main content
Medusa is headless, so Totebot can’t auto-discover the shopper’s cart the way it can on Shopify or WooCommerce. You add a small bridge to your storefront that tells the widget which cart the shopper is using. Once it’s in place, the agent and the in-chat product cards can read and update the same cart the shopper sees. There are three steps: load the widget, expose the shopper’s cart, and react to the agent’s cart changes. Steps 2 and 3 are implemented together — see the complete examples for your stack.
Allow your storefront’s origin (CORS) — required. The widget reads the shopper’s cart directly from your Medusa server (the baseUrl you set below), so your storefront’s origin must be in your Medusa server’s STORE_CORS. If it isn’t, cart reads fail with a browser CORS error and the in-chat cart stays empty. Most storefronts are already listed (your own site needs it to load the cart at all); add yours if it isn’t — for example STORE_CORS=https://yourstore.com,https://www.yourstore.com — then restart Medusa.

1. Load the widget

Inject the widget script dynamically once the page has loaded:

2. Expose the cart

Tell the widget which cart the shopper is using by setting window.__totebotMedusa and dispatching totebot:medusa-cart. cartId is not a value you hard-code — read it from wherever your storefront keeps the shopper’s current cart (a cookie, your cart store, localStorage, etc.). Leave it undefined when the shopper has no cart yet; the agent creates one on first add and hands it back via the totebot:medusa-cart-adopt event (step 3). Push on load, and again whenever your cart changes so the agent always sees the latest cart. The lines marked YOU: are where you plug in your own cart logic — Totebot can’t know how your storefront stores its cart.
This snippet shows the shape. For a full implementation wired to your stack, see the complete examples.

3. Live refresh & cart bootstrap

When the agent (or an in-chat add) changes the cart, the write happens on Totebot’s backend (out-of-band from the shopper’s browser), so your storefront’s mini-cart would otherwise stay stale. Totebot pushes two browser events to close that gap — handle both in your storefront (see the complete examples):
  • totebot:medusa-cart-refresh — fired after any cart write. Re-read your cart and re-render your mini-cart. This is the Medusa analog of WooCommerce’s wc_fragment_refresh. Example: the shopper’s mini-cart says “2 items”; they ask the agent to add a tee; the icon updates to “3 items” without a reload. The rule: if your storefront reads the cart through a server-side cache that isn’t auto-invalidated on out-of-band writes, you must bust that cache in this handler — otherwise repeat writes to the same cart stay stale. If your storefront fetches the cart fresh each time (most client-side/SPA setups), there’s no cache to bust — just re-fetch and re-render. Concretely: the Next.js starter reads the cart with a force-cache tagged fetch, so router.refresh() alone is not enough — it also needs a revalidateTag server action (totebotRevalidateCart, shown in the Next.js example). Remix → revalidator.revalidate(); Nuxt/SvelteKit → re-run the cart load; a plain SPA → just re-fetch.
  • totebot:medusa-cart-adopt (carries the new cartId) — fired when the agent created a cart while the shopper had none. Persist the new cart id as the shopper’s cart (cookie / your cart store) so your own mini-cart and checkout point at the agent’s cart instead of an empty one.

4. Complete examples

Each example implements steps 2 and 3 in one place — pushing the cart out and reacting to the agent’s events — because a real bridge does both. They are illustrative: the helpers in commented blocks (getCartId, fetchCart, renderMiniCart, …) stand in for your storefront’s real cart code. Swap them for yours; the Totebot-facing parts (window.__totebotMedusa, the event names, the dispatch) stay exactly as written.
On the official Medusa Next.js starter the cart id lives in an httpOnly cookie, so you read it server-side in the layout and pass it to a small client bridge component. Adoption uses a server action because only the server can write that cookie.src/app/layout.tsx:
src/modules/totebot/totebot-adopt-cart.ts (adopts the agent’s cart and busts the starter’s cart cache):
src/modules/totebot/totebot-medusa-bridge.tsx:

Notes

  • Checkout URL on the Medusa Next.js starter: the starter’s /checkout page renders nothing until it knows which step to show — it hangs on a “Shipping Address” spinner without a step query param (its own checkout button always adds one). Set checkoutUrl to https://your-store.com/<region>/checkout?step=address, not …/checkout. Custom storefronts: point checkoutUrl at whatever URL actually renders your checkout.
  • The two totebot:medusa-cart-* events are framework-agnostic — Totebot only dispatches plain DOM events. How you react is up to your stack: Next.js needs revalidateTag + router.refresh() (shown above); Remix uses revalidator.revalidate(); Nuxt/SvelteKit re-run the cart load; a plain SPA just re-fetches the cart. Only the handler is stack-specific, never the Totebot side.
  • In a SPA that mutates the cart without navigation, dispatch totebot:medusa-cart after your own cart ops so the widget picks up changes.
  • Region: the agent binds every new cart to a region (currency/tax/shipping). On a starter storefront you need nothing — Totebot derives the region from the /<countryCode>/ segment of the page URL (matched against your /store/regions). Custom storefronts that don’t carry a country in the URL may set regionId on window.__totebotMedusa so the agent bootstraps the cart in the right region; otherwise it falls back to your store’s first region.
  • publishableKey is public by design (safe to expose client-side).
  • Only the server can write the httpOnly _medusa_cart_id cookie, so adoption uses a server action (Next.js example). Totebot can’t write your cart cookie from another origin — it can only tell your storefront which cart to adopt.