Storefront AI agent
How the storefront AI assistant works, how to connect your own agent, and what your agent can do.
Overview
The storefront AI assistant is a chat widget injected on your store. When a shopper sends a message,
it streams to your configured provider through our endpoint
POST /api/store/modules/ai-assistant/stream, and the widget renders the reply. Replies support
Markdown and optional tappable choices (see Interactive messages).
You configure it per store under Modules → AI Assistant: enable it, pick a provider, and (optionally) let the agent read your catalog.
Providers
Two ways to power the assistant:
| Provider | What it is | Use when |
|---|---|---|
| Claude / DeepSeek (BYOK) | We run the agent loop with your own model API key; our built-in store tools are available. | You want a shopping assistant over your catalog with minimal setup. |
| External agent (bring your own agent) | We proxy the conversation to your own agent API and stream its reply back. Your agent stays authoritative. | You already have an agent (e.g. a specialized assistant) and want it hosted in the store UI. |
External agent API
With the External agent provider, we forward each turn to your endpoint and re-stream the reply. Request body:
POST <your endpoint>?<optional query params>
Content-Type: application/json
{
"messages": [ { "role": "user", "content": "..." }, ... ],
"session_id": "<conversation id>",
"mcp": { "url": "...", "headers": { ... } } // present only if "expose store products" is on
}
You may set one auth header (name + value) and extra query params / body fields in the config. Respond with a streamed body in one of these formats:
| Stream format | Meaning | Notes |
|---|---|---|
text (recommended) |
Plain UTF-8 text; we re-wrap it into the widget stream. | Library-agnostic — your backend/AI-SDK version never has to match ours. |
sse-text |
SSE frames carrying plain text. | Also library-agnostic (transport convention only). |
ai-sdk-ui |
Verbatim Vercel AI SDK UI-message stream. | Reserved / not enabled. Requires the AI SDK at a protocol compatible with ours (ai@7). |
Prefer text: because we only read raw bytes, there is no coupling between your AI-SDK version and
ours. Only ai-sdk-ui would impose a version contract.
Per-page endpoints
Different pages can talk to different agents, for example one agent per landing page. Under Per-page endpoints, map a page path to its own endpoint URL:
| Path | Matches |
|---|---|
/summer-sale |
Exactly that page (a trailing slash, query string, or hash is ignored). |
/lp/* |
/lp and every page under it (/lp/shoes, /lp/vip/gold), but not /lpx. |
The most specific match wins: an exact path beats any /* rule, and a longer prefix beats a shorter one.
Pages with no match use the default endpoint URL. A rule only changes the URL; the auth header, query
params, body fields, and stream format are shared. The request body is the same for every endpoint, and
the shopper's conversation carries over as they move between pages.
Store products via MCP
Your store exposes a read-only MCP endpoint so an agent can look up your catalog. It lives on your store's own domain:
https://<your-store-domain>/api/store/mcp
It's scoped to your store by domain — no key needed when connecting via your store's URL. It's a standard Streamable-HTTP MCP server exposing:
| Tool | Does |
|---|---|
searchProducts |
Search published products by text / collection. |
getProduct |
Full details for one product by slug. |
listCollections |
List product collections. |
getStoreInfo |
Public store info (name, branding, currencies). |
When "expose store products" is on for an external agent, we include the binding in each request so your agent can connect:
"mcp": {
"url": "https://<your-store-domain>/api/store/mcp",
"headers": { "x-publishable-key": "pk_live_…" }
}
Fail-open: the store tools are optional enrichment. Your agent should connect the MCP client with a short timeout and continue normally if it's unreachable — the MCP endpoint being down must never break the conversation.
Interactive messages
Markdown: assistant messages render as sanitized Markdown — headings, bold, lists, rules, tables, links, inline code (inline HTML is sanitized to a safe subset). Just emit normal Markdown.
Tappable choices: to let the shopper pick from a fixed set, emit a fenced select block. The
widget renders it as chips and hides the raw block:
```select multi min=1 max=3
1. ⚡ Energy & Performance
2. 🌿 Gut & Digestion
3. 😴 Sleep & Relaxation
```
- Info string:
single(default) ormulti; optionalmin=N/max=N. - Items:
N. label(or- label, auto-numbered). Emojis allowed. - On tap, the shopper's reply arrives as the chosen numbers (e.g.
1 3) — a normal user message. Parse it as their selection. - An agent that doesn't emit the block still works: it renders as a numbered list and the shopper types.
Action buttons (call-to-action): emit a fenced action block — one button per line. Don't emit
raw HTML buttons/cards; they're stripped.
```action
View my formula
Shop the range | https://store.example.com/shop
```
- A plain line is a button whose tap sends that text back as the shopper's message.
label | <url>(https://…,/path,mailto:,tel:) opens a link in a new tab.- Buttons are themed with the store's accent automatically.
Redirect: end a reply with a fenced navigate block holding one store path, and the widget takes the
shopper there in the same tab once the reply finishes. Only pages on your own store are allowed.
```navigate
/products/energy-formula
```
Our built-in models emit select, action and navigate automatically. An external agent enables them by adding
the blocks to its own system prompt — no code change.