# AI Assistant — full corpus
> Every English page of the site as Markdown, in llms.txt order. Per-page twins: append `.md` to any page URL.
---
title: "AI Assistant — Busy day? You’ve got a mate."
description: "An AI assistant that learns from your website, answers questions, helps customers choose and hands the hot ones to your team — under your own name, on your own web address, day and night."
last_updated: "2026-09-24T05:30:42+03:00"
---
# AI Assistant — Busy day? You’ve got a mate.
Source: https://busymate.ai/
Last modified: 2026-09-24T05:30:42+03:00
An AI assistant that learns from your website, answers questions, helps customers choose and hands the hot ones to your team — under your own name, on your own web address, day and night.
## On this page's section
- [How the platform works | AI Assistant](https://busymate.ai/platform.md): How the platform works — everything is a setting you control
- [WebMCP: make your website something an assistant can use | AI Assistant](https://busymate.ai/webmcp.md): WebMCP — your website tells assistants what it can do; what changes for customers and how to enable it
- [Agent Ready v1: the web standard an AI agent reads | AI Assistant](https://busymate.ai/agent-ready.md): The AI Assistant Agent Ready v1 standard — six scored sections, ten requirements, and the evidence each check records
- [Meet the assistant your website could have | AI Assistant](https://busymate.ai/try.md): Shareable standalone assistant preview for any public website
- [Security and protocols | AI Assistant](https://busymate.ai/security.md): How data is kept separate, how sign-in is checked, how changes are confirmed
- [Who runs on AI Assistant](https://busymate.ai/customers.md): Businesses running on it today, with addresses you can open
- [Pricing | AI Assistant](https://busymate.ai/pricing.md): Platform pricing per workspace; the Shopify app plans
- [Contact | AI Assistant](https://busymate.ai/contact.md): Talk to the team
- [Services — support, sales, help in your app, everyday tasks | AI Assistant](https://busymate.ai/solutions.md): Customer support, sales and onboarding, help inside your app, everyday tasks, what customers keep asking, agencies
- [Documentation | AI Assistant](https://busymate.ai/docs.md): One AI assistant you brand as your own, for your own customers, connected to your own systems.
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md): Web embed, iOS, Android, desktop, sign-in contract, MCP client OAuth
- [Changelog | AI Assistant](https://busymate.ai/changelog.md): What shipped, release by release
- [Articles | AI Assistant](https://busymate.ai/articles.md): Researched reads on AI assistants, agents and your website
- [Privacy policy | AI Assistant](https://busymate.ai/privacy.md): The AI Assistant privacy policy.
- [Terms of service | AI Assistant](https://busymate.ai/terms.md): The AI Assistant terms of service.
- [Data processing addendum | AI Assistant](https://busymate.ai/legal/dpa.md): The AI Assistant data processing addendum.
- [App Store | AI Assistant](https://busymate.ai/store.md): Apps built for your mate
- [Artifacts | AI Assistant](https://busymate.ai/artifact.md): Public pages made by businesses' assistants
- [Free tools: check your website for AI assistants | AI Assistant](https://busymate.ai/tools.md): Free checker tools for any website owner — llms.txt, WebMCP readiness, MCP server
- [Integrations: every place your mate can answer | AI Assistant](https://busymate.ai/integrations.md): Every channel and connection the assistant answers on, grouped by category — what is ready today and what is on the way
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "How the platform works | AI Assistant"
description: "How AI Assistant works: one assistant that takes your brand, your content, your systems and your rules — from setup to human handoff. Change settings, not code."
last_updated: "2026-09-24T02:11:16+03:00"
---
# How the platform works | AI Assistant
Source: https://busymate.ai/platform
Last modified: 2026-09-24T02:11:16+03:00
How AI Assistant works: one assistant that takes your brand, your content, your systems and your rules — from setup to human handoff. Change settings, not code.
## On this page's section
- [White-label AI assistant for agencies and SaaS | AI Assistant](https://busymate.ai/platform/white-label.md): An assistant under your own name, on your own domain
- [Download AI Assistant Console | Desktop app](https://busymate.ai/desktop.md): Download the Console desktop app (macOS, Windows, Linux) — notifications, badge, deep links; machine manifest at /api/desktop/releases
- [Playground: every way your page and the assistant talk | AI Assistant](https://busymate.ai/playground.md): Playground — the live widget plus one instrument per page↔assistant interaction (open/preset, ask, events, page tools, MCP, identity, prompts, channels) with the exact code; markdown twin carries every snippet
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "White-label AI assistant for agencies and SaaS | AI Assistant"
description: "Launch an AI assistant under your own name and web address. Your brand, actions in your own systems, 14 languages and human handoff — included, not an add-on."
last_updated: "2026-09-19T18:23:03+03:00"
---
# White-label AI assistant for agencies and SaaS | AI Assistant
Source: https://busymate.ai/platform/white-label
Last modified: 2026-09-19T18:23:03+03:00
Launch an AI assistant under your own name and web address. Your brand, actions in your own systems, 14 languages and human handoff — included, not an add-on.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "WebMCP: make your website something an assistant can use | AI Assistant"
description: "Your website tells assistants what it can do — book, order, check a status, answer. What changes for your customers, what AI Assistant does on a WebMCP site, and how to enable it in an afternoon."
last_updated: "2026-09-19T19:16:27+03:00"
---
# WebMCP: make your website something an assistant can use | AI Assistant
Source: https://busymate.ai/webmcp
Last modified: 2026-09-19T19:16:27+03:00
Your website tells assistants what it can do — book, order, check a status, answer. What changes for your customers, what AI Assistant does on a WebMCP site, and how to enable it in an afternoon.
## On this page's section
- [Adopt WebMCP: a quick how-to and a copy-paste prompt for any assistant | AI Assistant](https://busymate.ai/webmcp/adopt.md): How to adopt WebMCP: four steps and a copy-paste prompt for any assistant
- [WebMCP tool inspector: see what an assistant can do on any website | AI Assistant](https://busymate.ai/webmcp/inspect.md): WebMCP tool inspector — the tools a given website publishes, labelled by where each answer came from
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Playground: every way your page and the assistant talk | AI Assistant"
description: "The real AI Assistant widget docked on the page and an instrument for every interaction — open and preset it, send prompts from the page, listen to its events, register page tools, call MCP tools, recognise visitors — with the exact code for each."
last_updated: "2026-09-23T16:47:50+03:00"
---
# Playground: every way your page and the assistant talk | AI Assistant
Source: https://busymate.ai/playground
Last modified: 2026-09-23T16:47:50+03:00
The real AI Assistant widget docked on the page and an instrument for every interaction — open and preset it, send prompts from the page, listen to its events, register page tools, call MCP tools, recognise visitors — with the exact code for each.
The real assistant is docked on this page. Pick an instrument, press its controls, and watch the panel react — then copy the exact code that did it. No account, nothing to install.
Reference: https://busymate.ai/docs/guides/widget-page-api
## 1. Open & preset
Show, hide and flip the panel; pin its colour scheme and language before the visitor sees it.
One tag mounts it; four calls drive it — no iframe of your own to size, no state of your own to keep.
### index.html
```html
```
### panel.js
```javascript
// The loader installs window.BusymateAI. Every call is safe before the frame
// has loaded — commands queue and deliver on load.
BusymateAI.open();
BusymateAI.close();
BusymateAI.toggle();
BusymateAI.isOpen(); // → true | false
```
### preset.js
```javascript
// Preset the panel from the page: colour scheme + language.
// Neither is persisted by the frame — your page owns them while it embeds it.
BusymateAI.setTheme("dark"); // "light" | "dark" | "system"
BusymateAI.setLocale("de"); // any BCP 47 tag the platform serves
BusymateAI.setLocale(null); // unpin — the frame follows the visitor again
BusymateAI.open();
```
### push.html
```html
```
Docs: https://busymate.ai/docs/guides/widget-page-api#open-and-preset
## 2. Page → chat
Send a prompt from anywhere on the page — a button, a card, a form — or just fill the composer and let the visitor press send.
The message takes the same path a typed one does, so nothing is faked and every guard still applies.
### ask.js
```javascript
// Open the panel and send a prompt — the same append path a typed message takes.
BusymateAI.ask("What can you do on this page?");
```
### prefill.js
```javascript
// Fill the composer only; the visitor reads, edits and presses send.
BusymateAI.ask("Book a table for two on Friday at 19:00", { submit: false });
```
### chips.html
```html
```
Docs: https://busymate.ai/docs/guides/widget-page-api#page-to-chat
## 3. Chat → page
The panel talks back: it tells the page when a session is live, when the visitor closed it, and when they clicked a link that belongs to your site.
A same-site link navigates your page in place — same tab, conversation intact — so the visitor never loses the thread.
### events.js
```javascript
// The frame posts busymate.ai.v1.* messages to your page. The loader already
// acts on every one of them; your page only listens to OBSERVE.
// audit docs-and-developer-path-08 (2026-09-18): derived, never a hardcoded
// literal — a workspace on its own custom domain serves the frame from a
// different origin than the one this doc happens to render on.
const ORIGIN = window.BusymateAI?.origin
?? new URL(document.currentScript?.src ?? document.querySelector('script[data-assistant]').src).origin;
window.addEventListener("message", (event) => {
if (event.origin !== ORIGIN) return;
const { type, ...data } = event.data ?? {};
if (typeof type !== "string" || !type.startsWith("busymate.ai.v1.")) return;
console.log(type, data);
// busymate.ai.v1.ready { visitorKind, displayClaims } a session is live
// busymate.ai.v1.close — ✕ pressed inside the frame
// busymate.ai.v1.navigate { href } a same-origin link was clicked
// busymate.ai.v1.open_url { url } any other link (new tab)
// busymate.ai.v1.resize_to { w, h } resize_end resize_by { dw, dh }
});
```
### navigate.js
```javascript
// A same-origin link in the chat navigates YOUR page in place (same tab).
// SPA routers already listening for popstate resync on their own; this
// dedicated event needs no router at all.
window.addEventListener("busymate:hostnavigate", (event) => {
const { href } = event.detail;
myRouter.push(new URL(href).pathname);
});
```
### open-state.js
```javascript
// The panel announces itself: an event for scripts, an attribute for CSS.
// is stamped from the first frame.
window.addEventListener("bmai:chat-open", (event) => {
console.log("open", event.detail.assistant, event.detail.reserved);
});
window.addEventListener("bmai:chat-close", () => {});
```
Docs: https://busymate.ai/docs/guides/widget-page-api#chat-to-page
## 4. Page tools
Declare what this page can do and your mate can do it — through WebMCP where the browser has it, through its own bridge everywhere else.
A tool that changes something asks the visitor first, inside the chat, before it runs.
### page-tools.js
```javascript
// Declare what THIS page can do. One call, both transports: the browser's
// own WebMCP where it exists, the assistant's bridge everywhere else.
BusymateAI.registerPageTools([
{
name: "set_page_theme",
description: "Switch this page between light and dark.",
inputSchema: {
type: "object",
properties: { theme: { type: "string", enum: ["light", "dark"], description: "The scheme to apply" } },
required: ["theme"],
additionalProperties: false,
},
annotations: { readOnlyHint: false, consequentialHint: true },
execute: async ({ theme }) => {
// Flip through your OWN theme mechanism (whatever sets color-scheme /
// your CSS variables) so every other themed control on the page agrees
// with what this tool just did — never write the DOM attribute alone.
const applied = theme === "light" || theme === "dark";
if (applied) myApplyTheme(theme);
// Return the RESULTING state, not just "ok": a caller with no applied
// flag to check against will report success it never confirmed.
return { ok: applied, applied, theme };
},
},
{
name: "get_cart",
description: "Read what is in the visitor's cart right now.",
inputSchema: { type: "object", properties: {}, additionalProperties: false },
annotations: { readOnlyHint: true },
execute: async () => ({ items: cart.items, total: cart.total }),
},
]);
```
### standard-surface.js
```javascript
// The same tools are on the STANDARD surface — any agent, not only ours.
const tools = await document.modelContext.getTools();
tools.map((t) => t.name); // → ["set_page_theme", "get_cart", …]
```
Docs: https://busymate.ai/docs/guides/page-tools
## 5. MCP
Behind the chat sits a Model Context Protocol server: the tools your mate can call and any other agent can discover, before any sign-in.
Connect your own server in the Console and the same conversation reaches your orders, bookings and tickets.
### tools-list.js
```javascript
// What the assistant on this page can call: the platform's public tools,
// discoverable before any sign-in (JSON-RPC over HTTP).
const res = await fetch("https://busymate.ai/mcp", {
method: "POST",
headers: { "content-type": "application/json", accept: "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list" }),
});
const { result } = await res.json();
result.tools.map((t) => t.name);
```
### ask-for-a-tool.js
```javascript
// From the page, a prompt that makes the assistant call one of those tools.
BusymateAI.ask("Is the platform up right now? Check the status.");
```
Docs: https://busymate.ai/docs/guides/connect-mcp-server
## 6. Identity
A visitor signed in to your product is recognised in the chat automatically; one who is not can sign in without leaving it.
Recognised visitors unlock the tools that touch their own data — orders, bookings, invoices — with nothing secret ever crossing the page.
### identity.js
```javascript
// Auto-connect: when the visitor is signed in to YOUR product, the chat
// knows who they are. Your backend mints a short-lived, one-time proof.
// Add getIdentity ONTO the object — never assign a fresh one to
// window.BusymateAI: the loader installs every call on THAT object, so
// replacing it takes open(), ask(), identityChanged() and signedOut() with it.
window.BusymateAI = window.BusymateAI || {};
window.BusymateAI.getIdentity = async () => {
const r = await fetch("/api/assistant-identity", { method: "POST", credentials: "include" });
return r.status === 401 ? null : r.json(); // { token, nonce } or anonymous
};
// Declaring it after the tag already loaded? Hand it over instead:
BusymateAI.configure({ getIdentity: window.BusymateAI.getIdentity });
// After YOUR login, token rotation or account switch — upgrades the live
// conversation in place, same thread:
BusymateAI.identityChanged();
// After YOUR logout — without this the visitor keeps a verified chat and the
// previous transcript until the tab closes:
BusymateAI.signedOut();
```
### late-identity.js
```javascript
// Auth that hydrates AFTER the chat launched anonymously — hand it over late.
BusymateAI.identify({ token, nonce }); // remounts as an identified launch
```
### ready.js
```javascript
// The frame tells the page which kind of visitor the session is for.
// (ORIGIN derived the same way as the "chat to page" listener above.)
window.addEventListener("message", (event) => {
if (event.origin !== ORIGIN || event.data?.type !== "busymate.ai.v1.ready") return;
console.log(event.data.visitorKind, event.data.displayClaims); // "anonymous" | "identified", { name?, … }
});
```
Docs: https://busymate.ai/docs/guides/identified-visitors
## 7. Prompts
A handful of prompts that show the range — grounded answers, a tool call, a hand-off, a page it makes for you. Press one; it runs.
Each one is a single call from the page, so the same button works on any site that carries the tag.
### prompt.js
```javascript
// Every example prompt on this page is one call — the same call your
// own "try it" buttons make.
BusymateAI.ask("Compare the plans and recommend one for a two-person shop.");
```
Docs: https://busymate.ai/docs/guides/widget-page-api#page-to-chat
## 8. Channels
The same assistant answers on your website, in your apps, and on the messaging channels your customers already use.
One workspace, one knowledge base, one hand-off inbox — every channel below is a live demo you can open now.
### full-page.js
```javascript
// The same assistant as a full page. For an ANONYMOUS visitor that is a
// plain link — the workspace address is public.
window.open("https://your-workspace-slug.busymate.ai/", "_blank", "noopener,noreferrer");
// To carry an IDENTIFIED visitor across (a different top-level site cannot
// read this page's storage), ask the loader to mint the hand-off. Both calls
// REJECT unless the getIdentity provider above is configured.
const url = await BusymateAI.hostedUrl("https://your-workspace-slug.busymate.ai/");
BusymateAI.openHosted("https://your-workspace-slug.busymate.ai/"); // new tab, same visitor
```
Docs: https://busymate.ai/integrations
## 9. Your site
Paste one tag before and everything on this page works on yours.
Start from a scan of your own site to see what your mate would already know — no account needed for the preview.
### index.html
```html
```
Docs: https://busymate.ai/docs/getting-started
---
---
title: "Pricing | AI Assistant"
description: "Platform pricing per workspace, based on what you use — your brand and domain included. AI Assistant for Shopify: Free, Starter, Growth and Scale plans."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Pricing | AI Assistant
Source: https://busymate.ai/pricing
Last modified: 2026-09-19T18:23:03+03:00
Platform pricing per workspace, based on what you use — your brand and domain included. AI Assistant for Shopify: Free, Starter, Growth and Scale plans.
## Platform
One price per workspace — each client, product or brand gets its own.
What drives the price: how many conversations, and what the AI models cost.
Your own brand and your own domain are included, not add-ons.
If we do not know a price, we show it as unknown — never as zero.
Metered by: conversations and modelSpend.
Talk to us: https://busymate.ai/contact. Partners and agencies: https://busymate.ai/solutions/agencies-white-label.
No public platform tier is published yet — talk to us for a workspace quote.
## AI Assistant for Shopify
Plans and monthly caps exactly as published in our app catalogue.
### Free — Free
- 25 AI resolutions/month, then routes to your team
- No credit card required
- Never switched off at the cap
### Starter — $19/month — 14-day free trial
- 38 AI resolutions included, then $0.49/resolution
- $200/month spend cap
- Unlimited seats
- Never switched off at the cap
### Growth — $99/month — 14-day free trial
- 225 AI resolutions included, then $0.44/resolution
- $1,000/month spend cap
- Unlimited seats
### Scale — $349/month — 14-day free trial
- 830 AI resolutions included, then $0.42/resolution
- $5,000/month spend cap
- Unlimited seats
- Priority support
At your plan's resolution cap the assistant keeps chatting but stops taking billable resolutions — new conversations route to your team, and you are never charged above the cap.
Full listing: https://busymate.ai/store/apps/busymate-ai-shopify
## What counts
What each word on the bill means.
### What is a conversation?
One chat between a customer and the assistant, on any surface — your website, the chat launcher, iOS, Android, desktop or a channel. Conversations are counted per workspace.
### What is model spend?
What the AI providers charge for the models your conversations use, counted per provider and shown per workspace and per user in the Console. If we do not know a price, we show it as unknown — never as zero.
### What counts as a resolution in the Shopify app?
A shopper conversation the assistant handled on its own, without your team — billed once per conversation, and only then. A handoff to your team, an "I'm not sure" reply, or a conversation nobody answers is never billed. Each plan includes a monthly number of resolutions, then charges per resolution up to the plan's monthly cap — that cap is the most Shopify will bill you in a month for this app, the plan's own fee included. The assistant is never switched off for billing.
### What happens when I reach my limit?
Two different caps, same rule — never a silent overspend. On AI Assistant for Shopify, your plan's resolution cap never switches the assistant off: it keeps chatting and new conversations route to your team. On the platform, your workspace's own conversation and spend caps (set in the Console) work the other way — the assistant stops at that cap, so it never silently overspends.
### How is the Shopify app billed, and what happens after the trial?
Shopify bills every paid plan on your regular Shopify invoice, never through us directly. A plan's free trial includes that plan's monthly resolutions from day one; nothing is charged until the trial ends, and you can change or cancel the plan from your Shopify admin at any time. Until you choose a plan, the app runs on the Free plan's limits.
### What is a seat?
A teammate who can sign in to a workspace's Console and Inbox. Where a plan lists "Unlimited seats," that workspace has no per-seat charge and no cap on how many teammates you add.
### What does "Priority support" mean?
A request from a workspace on a plan that lists "Priority support" is handled ahead of the standard queue, through the same support channel every workspace already uses.
### I run a Shopify store — can I also use the platform?
Yes. Your Shopify store keeps its own plan, billed through Shopify. Anything beyond it — another channel, another site, another brand — is a separate workspace. Talk to us and we'll set up the exact combination for your case.
### I run an agency with Shopify clients — how does this work?
Talk to us. We'll set up your Console with a workspace per client and confirm the billing that fits an agency running several client stores.
## Enterprise and agencies
One Console, many workspaces — each with its own brand, domain, systems, limits and usage. Talk to us about volume.
---
---
title: "Services — support, sales, help in your app, everyday tasks | AI Assistant"
description: "Every job, one assistant: customer support with hand-off, sales and onboarding, help inside your app, everyday tasks through your systems, customer insight and white-label for agencies — with voice, a team Inbox, integrations and site knowledge built in."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Services — support, sales, help in your app, everyday tasks | AI Assistant
Source: https://busymate.ai/solutions
Last modified: 2026-09-19T18:23:03+03:00
Every job, one assistant: customer support with hand-off, sales and onboarding, help inside your app, everyday tasks through your systems, customer insight and white-label for agencies — with voice, a team Inbox, integrations and site knowledge built in.
## On this page's section
- [AI customer support with human handoff | AI Assistant](https://busymate.ai/solutions/customer-support.md): Customer support that answers from your content and hands off to a person
- [AI sales and onboarding assistant | AI Assistant](https://busymate.ai/solutions/sales-onboarding.md): Sales and onboarding answers from your own product
- [Embed an AI assistant in your app | AI Assistant](https://busymate.ai/solutions/in-product-copilot.md): An assistant inside your app that acts for the signed-in user
- [Everyday tasks through your systems, confirmed first | AI Assistant](https://busymate.ai/solutions/operations-automation.md): Everyday tasks through your systems, confirmed first
- [Learn what customers keep asking | AI Assistant](https://busymate.ai/solutions/product-intelligence.md): Learn what customers keep asking, with the conversations as evidence
- [White-label AI for agencies and resellers | AI Assistant](https://busymate.ai/solutions/agencies-white-label.md): White-label AI for agencies and resellers
- [Voice support: customers talk to your assistant | AI Assistant](https://busymate.ai/solutions/voice-support.md): Voice support — customers speak to the assistant, same answers and rules as typing
- [Human hand-off and a team Inbox | AI Assistant](https://busymate.ai/solutions/handoff-inbox.md): Human hand-off and the team Inbox — a person joins the same conversation
- [An assistant that knows your website | AI Assistant](https://busymate.ai/solutions/site-knowledge.md): Site knowledge — answers from your own website and text, with sources
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "AI customer support with human handoff | AI Assistant"
description: "Answers from your own content, real help with orders and accounts for signed-in customers, and a person who joins the same conversation — if you turn it on — in 14 languages."
last_updated: "2026-09-19T18:23:03+03:00"
---
# AI customer support with human handoff | AI Assistant
Source: https://busymate.ai/solutions/customer-support
Last modified: 2026-09-19T18:23:03+03:00
Answers from your own content, real help with orders and accounts for signed-in customers, and a person who joins the same conversation — if you turn it on — in 14 languages.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "AI sales and onboarding assistant | AI Assistant"
description: "Answers for prospects and new customers from your own docs, suggested first questions taken from your content, and a handoff to your sales team when a person should step in."
last_updated: "2026-09-19T18:23:03+03:00"
---
# AI sales and onboarding assistant | AI Assistant
Source: https://busymate.ai/solutions/sales-onboarding
Last modified: 2026-09-19T18:23:03+03:00
Answers for prospects and new customers from your own docs, suggested first questions taken from your content, and a handoff to your sales team when a person should step in.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Embed an AI assistant in your app | AI Assistant"
description: "An assistant inside your web, iOS, Android or desktop app that knows your docs, the user's own data and what they are allowed to do — and asks before it changes anything."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Embed an AI assistant in your app | AI Assistant
Source: https://busymate.ai/solutions/in-product-copilot
Last modified: 2026-09-19T18:23:03+03:00
An assistant inside your web, iOS, Android or desktop app that knows your docs, the user's own data and what they are allowed to do — and asks before it changes anything.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "White-label AI for agencies and resellers | AI Assistant"
description: "One Console, many clients. Each client gets its own workspace with its own brand, domain, systems, allowed models, limits and usage. Let an AI agent set up each client. Talk to us."
last_updated: "2026-09-19T18:23:03+03:00"
---
# White-label AI for agencies and resellers | AI Assistant
Source: https://busymate.ai/solutions/agencies-white-label
Last modified: 2026-09-19T18:23:03+03:00
One Console, many clients. Each client gets its own workspace with its own brand, domain, systems, allowed models, limits and usage. Let an AI agent set up each client. Talk to us.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Adopt WebMCP: a quick how-to and a copy-paste prompt for any assistant | AI Assistant"
description: "Five reasons, four steps and one prompt: paste it into the assistant you already use and it audits your site, proposes the tools and writes the code — for Shopify, WordPress, Webflow and plain HTML."
last_updated: "2026-09-15T15:55:25+03:00"
---
# Adopt WebMCP: a quick how-to and a copy-paste prompt for any assistant | AI Assistant
Source: https://busymate.ai/webmcp/adopt
Last modified: 2026-09-15T15:55:25+03:00
Five reasons, four steps and one prompt: paste it into the assistant you already use and it audits your site, proposes the tools and writes the code — for Shopify, WordPress, Webflow and plain HTML.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Agent Ready v1: the web standard an AI agent reads | AI Assistant"
description: "Can an AI find you, understand you, trust you and do useful work with you? The six sections, the ten requirements, the one-URL Markdown contract and the evidence behind every verdict."
last_updated: "2026-09-19T19:16:27+03:00"
---
# Agent Ready v1: the web standard an AI agent reads | AI Assistant
Source: https://busymate.ai/agent-ready
Last modified: 2026-09-19T19:16:27+03:00
Can an AI find you, understand you, trust you and do useful work with you? The six sections, the ten requirements, the one-URL Markdown contract and the evidence behind every verdict.
Version 1.2. Published by AI Assistant as `AI Assistant Agent Ready v1`.
## Sections and weights
The six sections share 100 points. Each one grades one layer of what an agent meets on a website.
| Section | Layer | Points | What it asks |
| --- | --- | --: | --- |
| Discover | discovery | 15 | Can an AI agent find your content? |
| Understand | semantics | 15 | Does an agent understand your business correctly? |
| Read | content | 20 | Can an agent consume your content efficiently? |
| Act | actions | 20 | Can an agent take actions on your website? |
| Connect | connectivity | 20 | Can external agents call your systems? |
| Trust | safety | 10 | Can an agent authenticate, confirm actions, and reach a human? |
## Requirements
Ten clauses decide readiness. Everything else the catalogue carries is a diagnostic.
- **R1** (content) — Important content is present in the initial HTML. Proven by: initial_html_content.
- **R2** (content) — Pages can expose a Markdown representation of themselves. Proven by: markdown_representation.
- **R3** (discovery) — The site publishes a sitemap. Proven by: sitemap.
- **R4** (discovery) — The site publishes an llms.txt. Proven by: llms_txt.
- **R5** (discovery) — The site publishes a machine-readable capability manifest. Proven by: capability_manifest.
- **R6** (semantics) — Pages declare their language and their canonical URL. Proven by: language_canonical.
- **R7** (semantics) — The site publishes structured business or product metadata. Proven by: business_metadata.
- **R8** (actions) — Web actions are declared when the page offers any. Proven by: page_tools.
- **R9** (actions) — Backend agent tools or an API are declared when the service offers any. Proven by: programmatic_api.
- **R10** (safety) — Authentication, authorization and human hand-off are explicit. Proven by: human_handoff, auth_explicit.
## Checks
35 checks make up the catalogue; 8 of them carry no weight — an unsettled convention is reported when found and never deducted when absent. A check whose `appliesWhen` is false leaves the denominator instead of counting against the site.
- `sitemap` (discover, R3, 3 pts) — A sitemap lists the pages an agent should read. Expects: An XML sitemap (or a sitemap index) with at least one .
- `llms_txt` (discover, R4, 4 pts) — llms.txt points an agent at the content that matters. Expects: GET /llms.txt returns text with at least one link.
- `capability_manifest` (discover, R5, 3 pts) — agents.json joins content, interfaces, authentication and contact in one card. Expects: A JSON manifest at /.well-known/agents.json or /agents.json.
- `manifest_twin_parity` (discover, diagnostic, 1 pts) — When agents.json is published at both paths, the two copies agree. Expects: /.well-known/agents.json and /agents.json answer with byte-identical bodies when both exist.
- `sitemap_xml_valid` (discover, diagnostic, 1 pts) — The sitemap is a real urlset or sitemapindex document whose loc entries point at the site's own origin. Expects: 200, a or root element, and at least one same-origin . A redirect to another path is reported with the target.
- `robots_ai_crawlers` (discover, diagnostic, 2 pts) — robots.txt lets AI search and user-requested retrieval reach your pages. Expects: robots.txt does not block search/citation or user-requested AI agents from /.
- `discovery_link_headers` (discover, diagnostic, 1 pts) — HTTP Link headers tell an agent the site MEANT to expose these documents. Expects: Link: ; rel="describedby" and ; rel="alternate".
- `robots_training_policy` (discover, diagnostic, optional) — The site states, either way, whether its pages may be used for model training. Expects: Optional: robots.txt names model-training crawler tokens. Opting out is a valid answer and costs nothing.
- `llms_full_txt` (discover, diagnostic, optional) — llms-full.txt carries the whole corpus in one file. Expects: Optional: GET /llms-full.txt returns text.
- `sitemap_md` (discover, diagnostic, optional) — sitemap.md is a human- and agent-readable index. Expects: Optional: GET /sitemap.md returns markdown.
- `agents_md` (discover, diagnostic, optional) — AGENTS.md is a prose companion to the manifest. Expects: Optional: GET /AGENTS.md returns markdown.
- `language_canonical` (understand, R6, 5 pts) — The page states what language it is in and which URL is canonical. Expects: and (or a canonical Link header).
- `business_metadata` (understand, R7, 6 pts) — JSON-LD says who you are and what you sell, in a vocabulary every agent already parses. Expects: JSON-LD with an identity type (Organization, LocalBusiness, WebSite, Store) or a Product/Offer.
- `structured_data_fields` (understand, diagnostic, 3 pts) — The identity node carries real fields, not just a bare @type. Expects: An identity node (Organization, Product, …) with a name, a description and a url.
- `open_graph` (understand, diagnostic, 1 pts) — Open Graph gives a title, a description and an image to anything that previews a link. Expects: og:title, og:description and og:image.
- `content_freshness` (understand, diagnostic, optional) — Content nodes say when they last changed, so an agent can tell fresh from stale. Expects: Optional: CreativeWork / DataFeedItem nodes carry dateModified (or datePublished).
- `initial_html_content` (read, R1, 14 pts) — The content is in the served HTML, not assembled later by a script. Expects: One
, a landmark and real prose in the first response.
- `markdown_representation` (read, R2, 4 pts) — The SAME URL serves Markdown to a client that asks for it. Expects: GET the page with Accept: text/markdown → text/markdown and Vary: Accept.
- `markdown_frontmatter` (read, diagnostic, 1 pts) — The Markdown carries its own canonical URL, language and last-updated date. Expects: YAML frontmatter with canonical, language and updated (or title/description/last_updated).
- `markdown_alternate_link` (read, diagnostic, 1 pts) — The page advertises its Markdown alternate so an agent does not have to guess. Expects: or the same as a Link header.
- `page_tools` (act, R8, 13 pts) — The page registers its own actions as tools an agent can call in the browser. Expects: WebMCP tools on document.modelContext / navigator.modelContext, on the pages scanned.
- `page_tool_catalog` (act, diagnostic, 4 pts) — A static catalogue lets an agent read the page's tools without executing it. Expects: A WebMCP catalog document linked from the page or at a well-known path.
- `page_tools_policy` (act, diagnostic, 3 pts) — Permissions-Policy leaves the page's tools governed — by the default allowlist or by an explicit one. Expects: No tools= directive (the draft default is self), or an explicit tools= allowlist. A wildcard or a denial is the finding.
- `programmatic_api` (connect, R9, 8 pts) — At least one programmatic description of the service exists — MCP, OpenAPI, GraphQL or another declared API. Expects: One of: a live MCP endpoint, an OpenAPI document, a GraphQL schema, or an API declared in the manifest.
- `mcp_transport` (connect, diagnostic, 4 pts) — The MCP endpoint answers, negotiates a protocol revision and LISTS its tools. Expects: initialize and tools/list succeed over the transport the handshake actually ran on. Listing is not authorization to call.
- `access_requirements` (connect, diagnostic, 3 pts) — The service documents which capabilities need a token and which do not. Expects: A manifest, a WWW-Authenticate challenge or protected-resource metadata that states the access requirements.
- `oauth_metadata` (connect, diagnostic, 3 pts) — OAuth metadata discovery resolves, and advertises PKCE where a client must verify it. Expects: RFC 8414 / RFC 9728 metadata that resolves, with code_challenge_methods_supported. Dynamic client registration is optional.
- `authenticated_execution` (connect, diagnostic, 2 pts) — An authenticated call actually succeeds — proof that a listed tool is a usable tool. Expects: An authenticated tools/call that returns a result. This scanner holds no token for your server and never calls a stranger's tool, so this stays UNVERIFIED — reported, never counted as a pass or as your failure.
- `tool_annotation_coverage` (connect, diagnostic, optional) — Listed tools declare readOnlyHint, so a client can tell a read from a write before it calls. Expects: Optional: annotations on the listed tool definitions. Only meaningful from protocol revision 2025-03-26, which introduced them.
- `openapi_spec` (connect, diagnostic, optional) — An OpenAPI document describes the HTTP API. Expects: Optional when MCP or another API already describes the interface.
- `protocol_discovery_aliases` (connect, diagnostic, optional) — Optional well-known aliases (mcp.json, agent-card.json, api-catalog, …) are served cleanly or not at all. Expects: Optional: each alias is valid JSON or a clean non-200 — never the site's own HTML shell.
- `human_handoff` (trust, R10, 4 pts) — An agent can hand the conversation to a person. Expects: A contact email, phone or contact page an agent can quote.
- `contact_signal_sources` (trust, diagnostic, 1 pts) — A machine-readable contact signal exists independent of any stored crawl. Expects: A mailto: link, a /contact link, a JSON-LD contactPoint, or a Contact: line in llms.txt — any one is enough.
- `auth_explicit` (trust, R10, 3 pts) — The site states how an agent authenticates — or states that nothing needs authenticating. Expects: Authentication declared in the manifest, or OAuth metadata, or an explicit public posture.
- `action_confirmation` (trust, diagnostic, 2 pts) — A consequential action is actually confirmed with the person before it runs. Expects: A client/workflow test that drives a write action and observes the confirmation. This scanner does not run one against a stranger's service, so it stays UNVERIFIED — never a pass, and never a deduction blamed on you.
## Verdicts
- `pass` — what the clause expects came back.
- `partial` — some of it is in place; the evidence names the half that is not.
- `optional-found` / `optional-missing` — an unsettled convention, worth no points either way.
- `fail` — asked for, and nothing was there.
- `unverified` — the probe could not run. It keeps its weight and is listed apart; a refusal, a timeout or a redirect that never lands is never rounded up to a pass.
## Evidence
Every check records the call it made, the answer this document expects and the answer it actually got. Only headers that change the answer are kept, and credentials never are. A count of tools behind sign-in reads as unknown rather than zero when nobody signed in to count it.
## Related
- Grade a web address against this document: https://busymate.ai/ready.md
- What a site publishes for assistants to act on: https://busymate.ai/webmcp.md
- The developer guide: https://busymate.ai/developers.md
---
---
title: "Who runs on AI Assistant"
description: "Businesses running a live assistant under their own brand, at their own web address, with their own systems. Open one and talk to it. No logo wall, no numbers we did not measure."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Who runs on AI Assistant
Source: https://busymate.ai/customers
Last modified: 2026-09-19T18:23:03+03:00
Businesses running a live assistant under their own brand, at their own web address, with their own systems. Open one and talk to it. No logo wall, no numbers we did not measure.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Everyday tasks through your systems, confirmed first | AI Assistant"
description: "Routine requests, scheduled checks and multi-step tasks through your own systems. Every change is confirmed first, every run leaves a record, and silence never counts as success."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Everyday tasks through your systems, confirmed first | AI Assistant
Source: https://busymate.ai/solutions/operations-automation
Last modified: 2026-09-19T18:23:03+03:00
Routine requests, scheduled checks and multi-step tasks through your own systems. Every change is confirmed first, every run leaves a record, and silence never counts as success.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Learn what customers keep asking | AI Assistant"
description: "See what customers ask again and again, where the assistant gets stuck and what your docs are missing — grouped by topic, with the conversations as evidence, and a suggested fix."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Learn what customers keep asking | AI Assistant
Source: https://busymate.ai/solutions/product-intelligence
Last modified: 2026-09-19T18:23:03+03:00
See what customers ask again and again, where the assistant gets stuck and what your docs are missing — grouped by topic, with the conversations as evidence, and a suggested fix.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Voice support: customers talk to your assistant | AI Assistant"
description: "Let customers speak to your assistant instead of typing — on your website or inside your app, in their own language — with the same answers, the same rules and the same way to reach a person."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Voice support: customers talk to your assistant | AI Assistant
Source: https://busymate.ai/solutions/voice-support
Last modified: 2026-09-19T18:23:03+03:00
Let customers speak to your assistant instead of typing — on your website or inside your app, in their own language — with the same answers, the same rules and the same way to reach a person.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Human hand-off and a team Inbox | AI Assistant"
description: "When a customer asks for a person, or one of your rules decides, a teammate picks the conversation up in the Inbox, replies in the same thread and hands it back — with alerts, assignment rules and ratings."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Human hand-off and a team Inbox | AI Assistant
Source: https://busymate.ai/solutions/handoff-inbox
Last modified: 2026-09-19T18:23:03+03:00
When a customer asks for a person, or one of your rules decides, a teammate picks the conversation up in the Inbox, replies in the same thread and hands it back — with alerts, assignment rules and ratings.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "An assistant that knows your website | AI Assistant"
description: "Point it at your website or paste in your own text. It answers only from that, shows its sources and says when it is not sure — try it on your own site before you sign up."
last_updated: "2026-09-19T18:23:03+03:00"
---
# An assistant that knows your website | AI Assistant
Source: https://busymate.ai/solutions/site-knowledge
Last modified: 2026-09-19T18:23:03+03:00
Point it at your website or paste in your own text. It answers only from that, shows its sources and says when it is not sure — try it on your own site before you sign up.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Integrations: every place your mate can answer | AI Assistant"
description: "One assistant across every channel, app and system your business runs on — 56 connections in 7 categories, 21 ready today and the rest honestly marked on the way."
last_updated: "2026-09-26T14:57:26+03:00"
---
# Integrations: every place your mate can answer | AI Assistant
Source: https://busymate.ai/integrations
Last modified: 2026-09-26T14:57:26+03:00
One assistant across every channel, app and system your business runs on — 56 connections in 7 categories, 21 ready today and the rest honestly marked on the way.
## See it in place
27 live demos — each a connection from this catalog running on a real invented brand; open the site, then take the guide and the code home.
- [Web chat widget](https://busymate.ai/demo/web.md): Northwind Coffee — the full-feature reference: grounded chat, page actions over WebMCP, an MCP server, a demo customer to sign in as, and hand-off to a person. — live at https://web.demo.busymate.ai — demonstrates Web chat
- [Shopify store](https://busymate.ai/demo/shopify.md): Northline Outdoor — a storefront mock-up of the Busymate AI Shopify app: grounded chat, store actions over WebMCP, an MCP server, and identified sign-in. — live at https://shopify.demo.busymate.ai — demonstrates Shopify
- [Site-scan quick start](https://busymate.ai/demo/scan.md): Paste any address and meet the assistant it becomes — grounded chat, a live AI-readiness scorecard, no sign-up. — live at https://scan.demo.busymate.ai — demonstrates Website crawl + Web chat
- [WhatsApp channel](https://busymate.ai/demo/whatsapp.md): Marlow's Kitchen — a faithful WhatsApp-style preview of your mate, grounded chat, an MCP server over the menu and floor plan, WebMCP booking actions, and identified-guest reservations. — live at https://whatsapp.demo.busymate.ai — demonstrates WhatsApp
- [Telegram channel](https://busymate.ai/demo/telegram.md): Nomad Circuits — a faithful Telegram-style preview of your mate, grounded chat, an MCP server over the catalogue and repair bench, WebMCP shop actions, and identified-customer order/repair lookup. — live at https://telegram.demo.busymate.ai — demonstrates Telegram
- [Slack channel](https://busymate.ai/demo/slack.md): Patchwell — an IT helpdesk for small teams, answered inside Slack: a live Slack-style view of your mate, grounded chat, an MCP server over the service catalogue and tickets, WebMCP helpdesk actions, and identified-customer ticket lookup. — live at https://slack.demo.busymate.ai — demonstrates Slack
- [WooCommerce store](https://busymate.ai/demo/woo.md): Fernweh Supply Co. — a real WooCommerce shop with a live catalogue, a signed-in customer, real orders and an open REST API to drive it all. — live at https://woo.demo.busymate.ai — demonstrates WooCommerce
- [Discord channel](https://busymate.ai/demo/discord.md): Pixelforge Games — an indie studio's player desk shown the way it reads in a Discord #support channel: grounded chat, an MCP server over the games and patch notes, WebMCP page actions, a bug-report card and identified-player library and refunds. — live at https://discord.demo.busymate.ai — demonstrates Discord
- [Microsoft Teams channel](https://busymate.ai/demo/teams.md): Bramble & Co. — an HR and benefits desk answered in a Teams-style chat pane: grounded handbook answers, an MCP server over benefits and payroll, WebMCP page actions, and identified leave balances. — live at https://teams.demo.busymate.ai — demonstrates Microsoft Teams
- [Messenger & Instagram channels](https://busymate.ai/demo/meta.md): Sol & Salt Swimwear — one assistant answering Messenger and Instagram DMs: faithful previews of both threads, a grounded collection and size guide, an MCP server over orders and returns, WebMCP page actions, and identified-customer order lookup. — live at https://meta.demo.busymate.ai — demonstrates Facebook Messenger + Instagram
- [WordPress site](https://busymate.ai/demo/wordpress.md): Larkspur Studio — a branded WordPress business site running the real Busymate AI plugin: grounded chat, WebMCP page actions, an MCP server and an inline assistant block, not just a script tag. — live at https://wordpress.demo.busymate.ai — demonstrates WordPress
- [Ghost site](https://busymate.ai/demo/ghost.md): The Meridian Line — a branded Ghost publication with a native Members sign-in and a genuinely live assistant: grounded chat, WebMCP page actions, its own MCP server. — live at https://ghost.demo.busymate.ai — demonstrates Ghost
- [Squarespace site](https://busymate.ai/demo/squarespace.md): Quiet Pines Yoga — a real Squarespace trial site with a live grounded assistant, proven answering real class prices. — live at https://squarespace.demo.busymate.ai — demonstrates Squarespace
- [HubSpot desk](https://busymate.ai/demo/hubspot.md): Salterwick Property — a lettings agency whose assistant captures an enquiry as a record, not a paragraph: grounded fee answers, an in-chat form card, WebMCP page actions, its own MCP server and identified landlord lookups. — live at https://hubspot.demo.busymate.ai — demonstrates HubSpot
- [Zendesk desk](https://busymate.ai/demo/zendesk.md): Braidwater Rail — a regional railway passenger desk: grounded delay-repay answers from the published conditions, a claim opened as a structured ticket in an in-chat form card, WebMCP page actions, its own MCP server and identified passenger lookups. — live at https://zendesk.demo.busymate.ai — demonstrates Zendesk
- [Salesforce desk](https://busymate.ai/demo/salesforce.md): Orrery Instruments — an instrument maker whose applications desk narrows a specification from published data and captures it as a structured enquiry: grounded technical answers, an in-chat form card, WebMCP page actions, its own MCP server and identified customer lookups. — live at https://salesforce.demo.busymate.ai — demonstrates Salesforce
- [Intercom desk](https://busymate.ai/demo/intercom.md): Quillon Metrics — a product-analytics tool whose assistant carries the first reply from the real documentation and starts a conversation as a record: grounded pricing and how-it-works answers, an in-chat form card, WebMCP page actions, its own MCP server and identified thread lookups. — live at https://intercom.demo.busymate.ai — demonstrates Intercom
- [Zoho desk](https://busymate.ai/demo/zoho.md): Wrenbury Dental — a practice with a published price list whose reception desk answers the cost question at 11pm and raises a query as a record: grounded treatment and membership answers, an in-chat form card, WebMCP page actions, its own MCP server and identified patient lookups. — live at https://zoho.demo.busymate.ai — demonstrates Zoho
- [Help Scout desk](https://busymate.ai/demo/helpscout.md): Blackthorn Cycles — a workshop that quotes before it starts: grounded answers from the published work list, a repair booked in as a structured record through an in-chat form card, WebMCP page actions, its own MCP server and identified job lookups. — live at https://helpscout.demo.busymate.ai — demonstrates Help Scout
- [Mailchimp desk](https://busymate.ai/demo/mailchimp.md): Hearth & Rind — a cheesemonger whose assistant changes what lands in your inbox in one message instead of a preference centre: grounded list and club answers, an in-chat form card, WebMCP page actions, its own MCP server and identified subscriber lookups. — live at https://mailchimp.demo.busymate.ai — demonstrates Mailchimp
- [Calendly desk](https://busymate.ai/demo/calendly.md): Halloway Chambers — a legal set whose clerks’ desk explains the fixed first-meeting fee and holds a slot as a structured booking: grounded consultation and fee answers, an in-chat form card with a real slot picker, WebMCP page actions, its own MCP server and identified diary lookups. — live at https://calendly.demo.busymate.ai — demonstrates Calendly
- [SMS channel](https://busymate.ai/demo/sms.md): Ridgeway Dental — a family practice whose front desk runs on text: a faithful message-thread preview of the live assistant, grounded prices and visit types, an MCP server over the appointment book, an in-chat booking card with a real slot picker, and identified-patient appointment lookups. — live at https://sms.demo.busymate.ai — demonstrates SMS
- [Phone channel](https://busymate.ai/demo/phone.md): Greycoat Heating — an out-of-hours heating firm whose line is answered at any hour: a call-screen preview of the live assistant, grounded call-out types and rates, an MCP server over the control-room board, an in-chat callback card, and identified-customer job lookups. — live at https://phone.demo.busymate.ai — demonstrates Phone calls
- [Apple Messages channel](https://busymate.ai/demo/apple-messages.md): Haldane Optical — an independent optician whose workshop answers in a business thread: a faithful Messages-style preview of the live assistant, grounded lens and repair answers, an MCP server over the bench, an in-chat enquiry card, and identified-customer thread lookups. — live at https://apple-messages.demo.busymate.ai — demonstrates Apple Messages for Business
- [Viber channel](https://busymate.ai/demo/viber.md): Danube Line Coaches — an overnight intercity operator whose passenger office answers where travellers already are: a faithful Viber-style preview of the live assistant, grounded fare and case rules, an MCP server over the office board, an in-chat case card, and identified-traveller case lookups. — live at https://viber.demo.busymate.ai — demonstrates Viber
- [LINE channel](https://busymate.ai/demo/line.md): Tsukumo Camera — a second-hand camera shop whose official account sends almost nothing and answers everything: a faithful LINE-style preview of the live assistant, grounded stock and bench answers, an MCP server over the follower list, an in-chat subscription card, and identified-follower notice lookups. — live at https://line.demo.busymate.ai — demonstrates LINE
- [WeChat channel](https://busymate.ai/demo/wechat.md): Yunhe Tea Company — a tasting room whose official account is the whole front door: a faithful WeChat-style preview of the live assistant, grounded answers on what is open this month, an MCP server over the seating book, an in-chat booking card with a real sitting picker, and identified-member seat lookups. — live at https://wechat.demo.busymate.ai — demonstrates WeChat
## Messaging channels
Where a customer writes to you — your site, the messaging apps, mail and the phone.
- [Web chat](https://busymate.ai/integrations/web.md) — ready today: Web chat — the embed launcher and full-page chat on your own website, ready today · live demo: https://busymate.ai/demo/web.md, https://busymate.ai/demo/scan.md
- [Telegram](https://busymate.ai/integrations/telegram.md) — in beta: Telegram — team alerts and replies today (beta); a bot that answers customers is on the way, previewed by the demo · live demo: https://busymate.ai/demo/telegram.md
- [Slack](https://busymate.ai/integrations/slack.md) — in beta: Slack — answer your team and community in a workspace channel · live demo: https://busymate.ai/demo/slack.md
- [Email](https://busymate.ai/integrations/email.md) — ready today: Email — a support inbox your mate reads and answers in-thread, with team hand-off
- [Discord](https://busymate.ai/integrations/discord.md) — coming soon: Discord — answer members in a server channel or DM · live demo: https://busymate.ai/demo/discord.md
- [Microsoft Teams](https://busymate.ai/integrations/teams.md) — coming soon: Microsoft Teams — answer staff and customers in the chat they use for work · live demo: https://busymate.ai/demo/teams.md
- [WhatsApp](https://busymate.ai/integrations/whatsapp.md) — coming soon: WhatsApp — answer customers on your WhatsApp Business number · live demo: https://busymate.ai/demo/whatsapp.md
- [Facebook Messenger](https://busymate.ai/integrations/messenger.md) — coming soon: Facebook Messenger — answer the messages your Facebook Page receives · live demo: https://busymate.ai/demo/meta.md
- [Instagram](https://busymate.ai/integrations/instagram.md) — coming soon: Instagram — answer Instagram direct messages to your business account · live demo: https://busymate.ai/demo/meta.md
- [SMS](https://busymate.ai/integrations/sms.md) — coming soon: SMS — answer plain text messages on a Twilio number · live demo: https://busymate.ai/demo/sms.md
- [Phone calls](https://busymate.ai/integrations/phone.md) — coming soon: Phone calls — a spoken conversation on your phone number, with transfer to a person · live demo: https://busymate.ai/demo/phone.md
- [Apple Messages for Business](https://busymate.ai/integrations/apple-messages.md) — coming soon: Apple Messages for Business — answer in the Messages app from Maps, Safari and Spotlight · live demo: https://busymate.ai/demo/apple-messages.md
- [Viber](https://busymate.ai/integrations/viber.md) — coming soon: Viber — answer subscribers of your Viber bot · live demo: https://busymate.ai/demo/viber.md
- [LINE](https://busymate.ai/integrations/line.md) — coming soon: LINE — answer on a LINE Official Account · live demo: https://busymate.ai/demo/line.md
- [WeChat](https://busymate.ai/integrations/wechat.md) — coming soon: WeChat — answer followers of a WeChat Official Account · live demo: https://busymate.ai/demo/wechat.md
## Apps and platforms
The apps you ship and the desktop software your team works in.
- [iOS app](https://busymate.ai/integrations/ios.md) — ready today: iOS app in-app support — the identity-bridged web chat inside your own iOS app, ready today
- [Android app](https://busymate.ai/integrations/android.md) — ready today: Android app in-app support — the identity-bridged web chat inside your own Android app, ready today
- [Desktop app](https://busymate.ai/integrations/desktop.md) — in beta: Desktop app — the Console in its own window with notifications; macOS, Windows and Linux, all in beta
## Websites and site builders
The web chat on the builder your site already runs on — one script, no plugin.
- [WordPress](https://busymate.ai/integrations/wordpress.md) — ready today: WordPress — a real, workspace-generated plugin (widget, inline block/shortcode, signed-in visitor recognition) · live demo: https://busymate.ai/demo/wordpress.md
- [Ghost](https://busymate.ai/integrations/ghost.md) — ready today: Ghost — the web chat embed via Code Injection, no plugin · live demo: https://busymate.ai/demo/ghost.md
- [Wix](https://busymate.ai/integrations/wix.md) — ready today: Wix — the web chat embed via Custom Code
- [Squarespace](https://busymate.ai/integrations/squarespace.md) — in beta: Squarespace — the web chat embed via Code Injection · live demo: https://busymate.ai/demo/squarespace.md
- [Webflow](https://busymate.ai/integrations/webflow.md) — in beta: Webflow — the web chat embed via the site's custom code
## Commerce
Stores and payments your mate reads from and, with a yes, acts in.
- [Shopify](https://busymate.ai/integrations/shopify.md) — in beta: Shopify — the open-source storefront app: catalogue, policies, order help, hand-off · live demo: https://busymate.ai/demo/shopify.md
- [WooCommerce](https://busymate.ai/integrations/woocommerce.md) — ready today: WooCommerce — catalogue answers and a signed-in customer's own order status on a WooCommerce store; returns are answered from your policy page and handed to a person · live demo: https://busymate.ai/demo/woo.md
- [BigCommerce](https://busymate.ai/integrations/bigcommerce.md) — in beta: BigCommerce — catalogue answers and order help on a BigCommerce store
- [Magento / Adobe Commerce](https://busymate.ai/integrations/magento.md) — coming soon: Magento — catalogue answers and order help on a Magento / Adobe Commerce store
- [Stripe](https://busymate.ai/integrations/stripe.md) — coming soon: Stripe — read a customer's invoices and subscriptions; refunds go to a person
## CRM, helpdesk and sales
The systems your team already keeps customers, tickets, leads and bookings in.
- [HubSpot](https://busymate.ai/integrations/hubspot.md) — coming soon: HubSpot — contacts, timeline entries and tickets written from conversations · live demo: https://busymate.ai/demo/hubspot.md
- [Zendesk](https://busymate.ai/integrations/zendesk.md) — coming soon: Zendesk — hand-offs become tickets with the transcript · live demo: https://busymate.ai/demo/zendesk.md
- [Salesforce](https://busymate.ai/integrations/salesforce.md) — coming soon: Salesforce — leads and cases written from conversations · live demo: https://busymate.ai/demo/salesforce.md
- [Freshdesk](https://busymate.ai/integrations/freshdesk.md) — coming soon: Freshdesk — hand-offs become tickets with the transcript
- [Intercom](https://busymate.ai/integrations/intercom.md) — coming soon: Intercom — import conversations and contacts, or hand off into Intercom · live demo: https://busymate.ai/demo/intercom.md
- [Pipedrive](https://busymate.ai/integrations/pipedrive.md) — coming soon: Pipedrive — leads and deals written from sales conversations
- [Zoho](https://busymate.ai/integrations/zoho.md) — coming soon: Zoho — contacts and leads in Zoho CRM, tickets in Zoho Desk · live demo: https://busymate.ai/demo/zoho.md
- [Help Scout](https://busymate.ai/integrations/helpscout.md) — coming soon: Help Scout — hand-offs land in a Help Scout mailbox · live demo: https://busymate.ai/demo/helpscout.md
- [Mailchimp](https://busymate.ai/integrations/mailchimp.md) — coming soon: Mailchimp — opt-ins added to an audience with conversation tags · live demo: https://busymate.ai/demo/mailchimp.md
- [Google Calendar](https://busymate.ai/integrations/google-calendar.md) — coming soon: Google Calendar — bookings written straight into a calendar
- [Calendly](https://busymate.ai/integrations/calendly.md) — coming soon: Calendly — event types offered and booked in the chat · live demo: https://busymate.ai/demo/calendly.md
## Knowledge sources
Where your mate reads your content from — every answer cites its source.
- [Website crawl](https://busymate.ai/integrations/site-crawl.md) — ready today: Website crawl — a robots-aware same-origin crawl of your help site, every answer cited · live demo: https://busymate.ai/demo/scan.md
- [Zendesk Help Center](https://busymate.ai/integrations/zendesk-help-center.md) — ready today: Zendesk Help Center — published Zendesk help articles as a knowledge source
- [Notion](https://busymate.ai/integrations/notion.md) — coming soon: Notion — shared Notion pages as a knowledge source
- [Files and PDFs](https://busymate.ai/integrations/files.md) — coming soon: Files and PDFs — PDF and document upload as a knowledge source
- [Google Drive](https://busymate.ai/integrations/google-drive.md) — coming soon: Google Drive — a Drive folder of Docs, Sheets and PDFs as a knowledge source
- [Confluence](https://busymate.ai/integrations/confluence.md) — coming soon: Confluence — Confluence spaces as a knowledge source
- [GitHub](https://busymate.ai/integrations/github.md) — coming soon: GitHub — a repository's docs folder or README as a knowledge source
## Automation and developers
Tools, workflows and the surfaces your own systems call — or are called from.
- [Your tools, over MCP](https://busymate.ai/integrations/mcp.md) — ready today: Your tools over MCP — your mate calls your systems; any MCP client can reach it
- [WebMCP page tools](https://busymate.ai/integrations/webmcp.md) — ready today: WebMCP page tools — a page's own actions as tools the assistant and any agentic browser can use
- [Webhooks](https://busymate.ai/integrations/webhooks.md) — ready today: Webhooks — signed HTTP calls to your endpoint on chosen events
- [REST API](https://busymate.ai/integrations/api.md) — ready today: REST API — a documented REST API with per-workspace keys
- [Zapier](https://busymate.ai/integrations/zapier.md) — coming soon: Zapier — conversation events trigger Zaps; Zaps can message the assistant
- [Make](https://busymate.ai/integrations/make.md) — coming soon: Make — conversation events start Make scenarios
- [n8n](https://busymate.ai/integrations/n8n.md) — coming soon: n8n — conversation events start n8n workflows, self-hosted or cloud
- [Google Sheets](https://busymate.ai/integrations/google-sheets.md) — coming soon: Google Sheets — a spreadsheet row per chosen event
- [Airtable](https://busymate.ai/integrations/airtable.md) — coming soon: Airtable — a record per chosen event in an Airtable base
- [Jira](https://busymate.ai/integrations/jira.md) — coming soon: Jira — hand-offs and reported bugs become Jira issues
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "WebMCP tool inspector: see what an assistant can do on any website | AI Assistant"
description: "Put in a web address and see the actions a site publishes for assistants — each name, what it does, what it needs, and whether it only reads or changes something."
last_updated: "2026-09-19T19:16:27+03:00"
---
# WebMCP tool inspector: see what an assistant can do on any website | AI Assistant
Source: https://busymate.ai/webmcp/inspect
Last modified: 2026-09-19T19:16:27+03:00
Put in a web address and see the actions a site publishes for assistants — each name, what it does, what it needs, and whether it only reads or changes something.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Security and protocols | AI Assistant"
description: "How AI Assistant keeps your data separate, checks who a customer is, asks before changing anything and never shows your keys again — with the exact standards named."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Security and protocols | AI Assistant
Source: https://busymate.ai/security
Last modified: 2026-09-19T18:23:03+03:00
How AI Assistant keeps your data separate, checks who a customer is, asks before changing anything and never shows your keys again — with the exact standards named.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Download AI Assistant Console | Desktop app"
description: "The desktop app for the people who answer: system notifications the moment a conversation needs a human, a badge for what is waiting, and links that open the exact conversation. Free, for every platform below."
last_updated: "2026-09-26T01:13:20+03:00"
---
# Download AI Assistant Console | Desktop app
Source: https://busymate.ai/desktop
Last modified: 2026-09-26T01:13:20+03:00
The desktop app for the people who answer: system notifications the moment a conversation needs a human, a badge for what is waiting, and links that open the exact conversation. Free, for every platform below.
## Download
### macOS
- **macOS (Apple silicon + Intel)** — v1.1.1 (build 19) · 6.5 MB · 2026-09-25 · signed and notarized by Apple · macOS 10.15 or later
https://busymate.ai/downloads/console-desktop/1.1.1/Busymate-AI_universal.dmg
SHA-256 `27f290f69455617168d5522ec9ce3f9153974cdaeca2a36a5ac948104964db21`
- **Mac App Store** — the same app, kept current by the App Store: https://apps.apple.com/app/busymate-ai/id6809772800
### Windows
- **Windows 10/11 (64-bit) — MSI** — v0.2.2 · 3.1 MB · 2026-09-08 · preview build, unsigned · Windows 10 1809 or later
https://busymate.ai/downloads/console-desktop/0.2.2/Busymate.AI_0.2.2_x64_en-US.msi
SHA-256 `b7b9c2fed45e0c3e977a3a118cc807cfb5814015d6608441a388b23dd63452ae`
- **Windows 10/11 (64-bit) — installer** — v0.2.2 · 2.2 MB · 2026-09-08 · preview build, unsigned · Windows 10 1809 or later
https://busymate.ai/downloads/console-desktop/0.2.2/Busymate.AI_0.2.2_x64-setup.exe
SHA-256 `f7715871ad5ddfcbcfbb4bcb90320c228b8cb99472bd17f95a1e3c7e65b62921`
### Linux
- **Linux (x86-64) — AppImage** — v0.2.2 · 81.2 MB · 2026-09-08 · preview build, unsigned · glibc 2.31 (Ubuntu 20.04) or later
https://busymate.ai/downloads/console-desktop/0.2.2/Busymate.AI_0.2.2_amd64.AppImage
SHA-256 `67db59940651b37d0911f869d4128e4defb6cea18b581a9b43215f06db143efb`
- **Debian / Ubuntu (x86-64) — .deb** — v0.2.2 · 3.5 MB · 2026-09-08 · preview build, unsigned · glibc 2.31 (Ubuntu 20.04) or later
https://busymate.ai/downloads/console-desktop/0.2.2/Busymate.AI_0.2.2_amd64.deb
SHA-256 `57e4fd5126b0888d9f524cff5ec7f086ce3e4f4c8d5ee02417b25a39c0f87ea5`
Every release, with its notes: https://github.com/serebano/busymate-ai-desktop/releases
## Release notes — macOS v1.1.1 (2026-09-25)
- The window's top bar is taller (40 px), with the window buttons centred in it and the same space above, below and beside every control.
- The Console draws its matching 40 px bar only for this app, so the bar and the window buttons always line up.
- The position of the window buttons can now be adjusted from busymate.ai without a new app download; a change applies the next time the app opens.
- Build 19 is the same app as build 18, numbered to match the Mac App Store: the download and the store now carry the same version and build.
Full notes: https://github.com/serebano/busymate-ai-desktop/releases/tag/v1.1.1-build19
## What's new since 2026-09-13
### In the app
- On a Mac the window buttons sit on the Console's own bar — one bar, the same air around every control.
### In the Console it opens
- The rail, the Inbox and the Overview follow the language you pick.
- Soft is the default look in light and in dark, and breadcrumbs read as a plain path, a slash between places.
- Inbox replies show as formatted text — bold, lists and links — never as raw markup.
- Every workspace wears its own icon in the switcher and the breadcrumb, the way its website shows it.
- Press ⌘K anywhere: search opens centred, groups its results by page and speaks your language.
- Setting up moved into the chat: name your website and your mate builds the assistant from it while you watch.
## Requirements
- macOS: macOS 10.15 or later · Apple silicon + Intel · 6.5 MB · signed and notarized by Apple
- Windows: Windows 10 1809 or later · 64-bit · 2.2 MB · preview build, unsigned
- Linux: glibc 2.31 (Ubuntu 20.04) or later · 64-bit · 81.2 MB · preview build, unsigned
## Questions
- **Is it safe to open?** Yes — the macOS build is signed with our Developer ID and notarized by Apple, so it opens like any other app. Every download lists its size and SHA-256 checksum.
- **Does it update itself?** Depends on the platform. Updates itself: macOS. Not yet — download a new version by hand: Windows 0.2.2, Linux 0.2.2.
- **Do Console updates need a reinstall?** No — the app is a window onto the web Console, so every Console improvement is there the next time you open it. Only the shell itself arrives as a new build.
- **Is it free?** Yes, on every plan; it signs in with your AI Assistant account.
---
---
title: "Meet the assistant your website could have | AI Assistant"
description: "Put in a web address and talk to the assistant that site could have — built from its own public pages, live in seconds, on a link you can share."
last_updated: "2026-09-26T01:13:20+03:00"
---
# Meet the assistant your website could have | AI Assistant
Source: https://busymate.ai/try
Last modified: 2026-09-26T01:13:20+03:00
Put in a web address and talk to the assistant that site could have — built from its own public pages, live in seconds, on a link you can share.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Documentation | AI Assistant"
description: "One AI assistant you brand as your own, for your own customers, connected to your own systems."
last_updated: "2026-09-18T21:44:06+03:00"
---
# Documentation | AI Assistant
Source: https://busymate.ai/docs
Last modified: 2026-09-18T21:44:06+03:00
**AI Assistant** is a white-label AI platform: you run **your mate** — an AI assistant that can use tools — as your own product, under your name, on your web address, for your customers, connected to your own systems (through MCP, an open standard). It answers everyone, acts for signed-in customers, and hands off to a person on your terms. AI Assistant brings the assistant; you bring the brand and the data.
If you have customers who need help, answers, or actions taken on their behalf, AI Assistant gives you a production assistant without building one.
## What your mate is
**Your mate** is the assistant. One shared assistant serves every business, but each workspace is fully separate and fully branded, so your customers only ever see *your* assistant. Your mate can:
- **Answer** questions in natural language, with streaming responses and Markdown formatting.
- **Act** by calling tools — your tools — to read and change your customers' data, always within permissions you control.
- **Hand off** to a person when a conversation needs one.
The same assistant, rendered as your brand. Nothing about your mate names our brand to your customers unless you want it to.
## What you get as a white-label
Diagram: Your customers reach your branded assistant — your mate — which calls your tools and can hand off to your team
- **Your own branded assistant.** A workspace with your name, logo, colors, welcome copy, and voice. See [Getting started](https://busymate.ai/docs/getting-started).
- **Your customers.** Your customers reach the assistant on your web address — signed in with your own sign-in, or as visitors if you allow it. Their conversations, history, and data stay inside your workspace.
- **Your tools.** Connect your own MCP server so your mate can act on your customers' data — look things up, make changes, run your workflows. See [Connecting your systems](https://busymate.ai/docs/connectors).
- **Human handoff.** When the assistant reaches its limit, a teammate can step into the same conversation. See [Human handoff](https://busymate.ai/docs/human-handoff).
- **Usage and analytics.** See how much your assistant is used, per AI provider and over time, against your workspace limit. See [Usage and analytics](https://busymate.ai/docs/usage).
- **Governance.** Choose which features are on, whether customers pick a model or get one default, and where your limits sit. See [Governance and models](https://busymate.ai/docs/governance).
## How your customers reach it
You choose the channel — often more than one:
- **A hosted page** on a web address you control (for example `assistant.yourdomain.com`), pointed at your workspace.
- **An embed** on your own site or app — your mate drops into a panel or a chat bubble, on your pages.
Either way, your customers see your brand, not ours. Setup is covered in [Getting started](https://busymate.ai/docs/getting-started).
## Where AI Assistant fits
AI Assistant is the same technology that powers our own assistant — and it already runs live for outside partners, not just for us. That is the point: **AI Assistant is universal.** There is no special-case code for any one business. Every white-label is just another workspace, configured, never hand-coded — which is exactly why your assistant is stable, upgradeable, and gets every improvement the platform ships.
> **A note on honesty.** These docs describe what the product does now, plainly. Where a capability is still being switched on for white-labels, the page says so and points you to the changelog — you should never be surprised by a gap.
### What is your mate?
The assistant every business runs, rendered as your brand. One shared assistant; each workspace fully separate, fully branded, so your customers only ever see your assistant.
### Do I need my own AI model?
No. The platform runs the models. You choose which ones your workspace may use and whether customers pick one or get a single default — see [Governance and models](https://busymate.ai/docs/governance).
### What do my customers see?
Your name, your colors, your web address or your embed, and answers from your content and your tools. Nothing names our brand unless you want it to.
### Where do I start?
[Getting started](https://busymate.ai/docs/getting-started) walks through the five steps — workspace, web address, sign-in, systems, publish — and the [guides](https://busymate.ai/docs/guides) cover each job end to end.
## Explore the section
- **[Getting started](https://busymate.ai/docs/getting-started)** — your workspace, your web address, your branding, embedding.
- **[The assistant experience](https://busymate.ai/docs/assistant-experience)** — what your customers actually see.
- **[Governance and models](https://busymate.ai/docs/governance)** — features, model choice, limits.
- **[Usage and analytics](https://busymate.ai/docs/usage)** — how much it's used, and against what limit.
- **[Human handoff](https://busymate.ai/docs/human-handoff)** — bringing a person into the chat.
- **[Connecting your systems (MCP)](https://busymate.ai/docs/connectors)** — let your mate act on your customers' data.
- **[Managing from chat](https://busymate.ai/docs/managing-from-chat)** — run your workspace conversationally.
---
---
title: "Getting started | AI Assistant"
description: "How to set up your assistant: your workspace, your web address, your branding, and adding the chat to your own site."
last_updated: "2026-09-07T18:42:34+03:00"
---
# Getting started | AI Assistant
Source: https://busymate.ai/docs/getting-started
Last modified: 2026-09-07T18:42:34+03:00
Getting your AI Assistant assistant live takes five steps — workspace, web address, sign-in, systems, publish — and you do them yourself in the Console. Open [Console → Integration](https://busymate.ai/console/integration) for your workspace and follow its live checklist. It is generated from your real settings, so its URLs, sign-in fields, code snippets and checks are always current.
## The five steps
Diagram: Setup flow: workspace, web address, sign-in, systems, publish
### 1. Your workspace
A **workspace** is your own space on the platform. It carries your name, a short slug, and your branding. Everything your customers do — conversations, history, connected accounts — stays inside your workspace and is never visible to any other. A platform owner can create the workspace in **Console → Platform → Tenants** or through the AI Assistant management MCP; the invited admin then completes the Integration checklist without anyone handing off.
### 2. Your web address
Your assistant is served at a **web address you choose**, mapped to your workspace:
- **To start**, every workspace gets its own `.busymate.ai` address.
- **Your own domain** (for example `assistant.yourdomain.com`) once you point it at the platform and it’s verified. From then on, your customers only ever see your domain.
Under the hood, an address resolves to exactly one workspace. An address we haven't mapped yet stays plain and unbranded — your branding appears only once your address is verified and switched on, so there's never a half-branded window in front of your customers.
### 3. Your customers' sign-in
Decide how your customers are known to the assistant:
- **Signed in as themselves** — you connect your own sign-in, and each customer reaches your mate as *them*, so your mate can act on *their* data (and only theirs).
- **Visitors** — if you'd rather let anyone chat without signing in, you can allow guest access. Visitors get answers and guidance but not actions on a specific customer's private data.
You can offer both: visitors at the front door, signed-in sessions for your customers.
### 4. Your systems (optional)
If you want your mate to *do* things — not just answer — connect your own **MCP server**. That's how your mate reads and changes your customers' data through your API, under your rules. This is optional: an answers-only assistant needs no tools at all. See [Connecting your systems](https://busymate.ai/docs/connectors).
### 5. Publish
When branding, web address, sign-in, and (optionally) systems are set, run the **checks** and **publish** from the Console. Publishing is versioned — a change you make is prepared, checked, then switched on, so what your customers see only ever moves forward to a complete setup. A missing sign-in setup, an unreachable connection, or an unsafe action rule blocks publishing with a clear reason.
## Embedding your mate on your own site
Beyond a hosted page, you can **embed** your mate directly into your product — a side panel or a chat bubble on your own pages. The embed runs on your site's own address (which we add to your workspace's allowed list), so the assistant lives right where your customers already are, still fully your brand. It's one script tag:
```html
```
Hosted page, embed, or both — it's your call, and you can add the embed later without redoing anything.
## What to prepare
- Your **product name** and brand assets (logo, colors, a short welcome message).
- The **web address** you want (a subdomain to start, or your own domain to verify).
- How your customers **sign in** — or whether you want guest access.
- Optionally, your **MCP server** URL if you want your mate to take actions.
### Do I need my own domain to start?
No. Every workspace answers at its `.busymate.ai` address immediately. Add your own domain when you are ready — see [Serve your assistant on your own domain](https://busymate.ai/docs/guides/custom-domain).
### Can I add the embed later?
Yes. Hosted page, embed, or both; adding the embed later changes nothing about the workspace.
### What blocks publishing?
A missing sign-in setup, an unreachable connection or an unsafe action rule. The checks name the blocker; the draft stays a draft.
### Who creates the workspace?
A platform owner, in Console → Platform → Tenants or through the management MCP. The invited admin then completes the Integration checklist without anyone handing off.
## Next
- **[The assistant experience](https://busymate.ai/docs/assistant-experience)** — what your customers see once you're live.
- **[Governance and models](https://busymate.ai/docs/governance)** — turn features on and set your model rules.
- **[Connecting your systems (MCP)](https://busymate.ai/docs/connectors)** — let your mate act on your customers' data.
---
---
title: "The assistant experience | AI Assistant"
description: "What your customers see — the full chat with history, projects, a model choice if you allow it, streaming answers, and a way to reach a person."
last_updated: "2026-09-04T13:14:52+03:00"
---
# The assistant experience | AI Assistant
Source: https://busymate.ai/docs/assistant-experience
Last modified: 2026-09-04T13:14:52+03:00
Your customers see a full chat under your brand: streaming answers, saved conversations and projects in a side rail, a model choice if you allow it, and a way to reach a person in the same conversation. Signed-in customers get their own history and account actions; visitors get answers and guidance. It works like the chat apps they already know, and nothing names our brand.
## The chat, end to end
Diagram: The assistant layout: a left rail with projects and conversation history, a central conversation that streams answers, and a composer with a model choice and a way to reach a person
- **Streaming, formatted answers.** Responses stream in live and render as rich text — headings, lists, tables, and code — not a wall of plain text.
- **Conversation history.** Every chat is saved. Your customers switch between conversations from a sidebar, reopen any one right where they left off, and each is auto-titled from what they asked. They can rename or delete their own conversations.
- **Projects.** Related conversations can be grouped into **projects** in the side rail, so a customer working on one topic keeps those chats together.
- **A model choice.** Customers can pick which model answers — if your rules allow it. If you pin one model for your workspace, the assistant stays on it. See [Governance and models](https://busymate.ai/docs/governance).
- **A way to reach a person.** When the assistant can't finish something, your customer can ask for a person, and a teammate steps into the same conversation. See [Human handoff](https://busymate.ai/docs/human-handoff).
## Signed in, or as a visitor
How much a customer sees depends on how you set up sign-in (see [Getting started](https://busymate.ai/docs/getting-started)):
- **Signed-in customers** get their own private history and — if you've connected your systems — actions taken on *their* account, scoped to just their data.
- **Visitors** (if you allow guest access) can still chat and get answers and guidance, without signing in. Visitors don't get actions on a specific customer's private data.
## It's your brand, everywhere
The name at the top, the colors, the welcome message, the avatar — all yours. The same assistant can run as a full page at your web address and as an embedded panel inside your product, and it looks and behaves the same in both. Your customers never see our brand; they see you.
> **Consistent light and dark.** The assistant renders correctly in both light and dark themes automatically, following each person's system setting.
### Do my customers need an account?
Not if you allow guest access. Visitors chat and get answers; signed-in customers also get private history and actions on their own account — see [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors).
### Can customers pick the model?
Under a "customers pick" rule, from your allowed list. Under a one-default rule the assistant stays on the model you pinned.
### Does it follow dark mode?
Yes. The assistant renders in light and dark automatically, following each person's system setting.
### Where does conversation history live?
Inside your workspace, keyed to the customer. A visitor's history stays with that visitor; a signed-in customer's follows them across devices.
## Next
- **[Governance and models](https://busymate.ai/docs/governance)** — decide which features are on and how models are chosen.
- **[Human handoff](https://busymate.ai/docs/human-handoff)** — how a person joins the chat.
- **[Connecting your systems (MCP)](https://busymate.ai/docs/connectors)** — let the assistant act, not just answer.
---
---
title: "Governance and models | AI Assistant"
description: "As a workspace admin: which features are on within your plan, whether customers pick a model or get one default, and your limits."
last_updated: "2026-09-18T21:44:06+03:00"
---
# Governance and models | AI Assistant
Source: https://busymate.ai/docs/governance
Last modified: 2026-09-18T21:44:06+03:00
Governance is where a workspace admin decides what the assistant may do within its limit. You switch features on or off — guest access, human handoff, actions, branding — choose whether customers pick a model or get one default from your allowed list, and set your limits. The controls live in your **Console** and apply to your workspace only.
## Features within your workspace limit
Your workspace limit sets a ceiling; within it, you turn features on or off for your workspace:
- **Guest access** — let anyone chat without signing in, or require sign-in.
- **Human handoff** — let customers reach a person, and give your team the Inbox. Off by default until you turn it on. See [Human handoff](https://busymate.ai/docs/human-handoff).
- **Actions** — whether the assistant can do things through your connected systems, and which. See [Connecting your systems](https://busymate.ai/docs/connectors).
- **Branding** — your name, logo, colors, and welcome copy.
Think of it as two layers: your **workspace limit** is the ceiling the platform sets; your **settings** are where you land inside it. You can move freely up to the ceiling, never past it.
Diagram: Your workspace limit sets a ceiling; your settings sit inside it; customer choices sit inside your settings
## Model choice: who picks the model
You control which model answers your customers, with two settings:
| Setting | What your customers see | Use it when |
|---|---|---|
| **Customers pick** | A model choice in the composer; each customer picks from the models you allow. | You want to offer a range and let power users decide. |
| **One default** | One model, pinned. The assistant stays on it and won't switch. | You want consistent cost and behavior for everyone. |
You also set an **allowed list** — the models available under either setting — so "customers pick" still means *your* shortlist, never the entire catalog.
The setting is **enforced end to end**: under **one default**, a request to switch models is refused, so a customer can't route around your choice. The picker only ever offers models on your allowed list.
> **Rolling out.** Under a one-default setting the composer will show the pinned model as a plain label with no picker at all — a small visual refinement. The setting itself is already enforced today; the picker just doesn't offer other models to switch to.
## Limits
Your workspace carries a usage limit. You can see how close you are on the [Usage and analytics](https://busymate.ai/docs/usage) page, per AI provider and over time, so a busy month never surprises you.
## Where these live
All of the above sit in your **Console** — a governance panel for model choice and limits, a branding panel, a connections panel, and a usage panel — each scoped to your workspace. Platform-wide settings (creating workspaces, verifying domains) stay with the platform team; your controls are your own workspace's.
### Can a customer switch models under one default?
No. The setting is enforced end to end: a request to switch is refused, so nobody routes around your choice.
### What is the allowed list?
Your shortlist of the catalog. Under "customers pick" the picker offers only those models; under one default the pinned model must be on it.
### Who can change these settings?
Your workspace admins, for your workspace only. Platform-wide settings — creating workspaces, verifying domains — stay with the platform team.
### How do I turn on human handoff?
Here, in Governance, then staff the Inbox — the steps are in [Set up human handoff](https://busymate.ai/docs/guides/human-handoff-setup).
## Next
- **[Usage and analytics](https://busymate.ai/docs/usage)** — track usage against your limit.
- **[The assistant experience](https://busymate.ai/docs/assistant-experience)** — what your rules look like to a customer.
- **[Managing from chat](https://busymate.ai/docs/managing-from-chat)** — the same controls, conversationally.
---
---
title: "Usage and analytics | AI Assistant"
description: "See how much your assistant is used — per AI provider, for today, the last 7 and 30 days, and all time — against your limit."
last_updated: "2026-09-18T21:44:06+03:00"
---
# Usage and analytics | AI Assistant
Source: https://busymate.ai/docs/usage
Last modified: 2026-09-18T21:44:06+03:00
The usage panel in your **Console** shows how much your assistant is used, per AI provider, for four periods — today, the last 7 days, the last 30 days and all time — against your workspace limit, with a small trend chart. If a price is unknown, the row says so instead of showing zero.
## What you see
The usage panel gives you, for your workspace:
- **Usage per AI provider.** Each AI provider your assistant uses is listed with its own totals, so you can see where the volume actually goes.
- **Four periods.** Every figure is shown for **today**, the **last 7 days**, the **last 30 days**, and **all time** — so a spike today reads differently from a steady month.
- **Used vs limit.** Your usage against your workspace limit, so you always know the headroom you have left.
- **A trend at a glance.** A small trend chart shows the shape of recent usage, so a change in pace is obvious without reading numbers.
Diagram: Usage panel: per-provider cards each showing today, 7-day, 30-day and all-time totals, plus a used-versus-limit bar and a trend sparkline
## Why it's broken down this way
Different providers cost and behave differently, and a single blended number hides the story. Splitting by provider and by window lets you answer the real questions: *Is today unusual? Are we trending toward the limit this month? Which provider drives the cost?* — at a glance, without exporting anything.
Usage ties directly to your [Governance and models](https://busymate.ai/docs/governance) settings: the models you allow and the choice you make shape which providers show up here and how fast usage grows.
### Why is usage split by provider?
Providers cost and behave differently. A single blended number hides which one drives the volume; the split answers it at a glance.
### What counts as usage?
Model spend per provider, per window, against your workspace limit. A row the platform cannot price is shown as unpriced.
### Where do the limits come from?
Your workspace limit sets the ceiling; your [Governance and models](https://busymate.ai/docs/governance) settings shape how fast usage grows.
### Can I see usage from chat or MCP?
Yes. Ask the signed-in assistant, or call the same workspace-scoped tools from any MCP client — see [Managing from chat](https://busymate.ai/docs/managing-from-chat).
## Next
- **[Governance and models](https://busymate.ai/docs/governance)** — set the model choice and limits that drive these numbers.
- **[The assistant experience](https://busymate.ai/docs/assistant-experience)** — what generates the usage in the first place.
---
---
title: "Human handoff | AI Assistant"
description: "How a teammate joins a conversation in the same chat, and how a customer asks to talk to a person."
last_updated: "2026-09-07T18:42:34+03:00"
---
# Human handoff | AI Assistant
Source: https://busymate.ai/docs/human-handoff
Last modified: 2026-09-07T18:42:34+03:00
Some conversations need a person. AI Assistant lets a customer ask for a human in the chat and lets a teammate step into the **same** conversation from the Inbox — no re-explaining, no new channel — then hand it back to the assistant. Handoff is opt-in, so your mate only offers a person you have actually staffed.
## How a customer asks for a person
Your customer simply says so. When they ask to talk to a person — "can I speak to someone", "get me a human", "I need an agent" — the assistant recognizes the request and raises a handoff, rather than trying to muddle through. The customer stays right where they are; nothing sends them to a separate widget or email.
## How your team joins
On your side, the **Inbox** collects conversations waiting for a person. A teammate opens one, sees the full history the customer and the assistant already built, and **takes over** — replying in the same conversation. To the customer it is seamless: the same conversation, now with a person answering.
Diagram: A customer asks for a person in the assistant chat; the conversation appears in your Inbox; a teammate takes over and replies in the same conversation
When the teammate is done, the conversation can return to the assistant — the handoff is a moment in the conversation, not a dead end.
## Assignment and alerts
Choose **manual claim**, **round robin**, or **least active** in the Console. The automatic modes consider only teammates who are marked available and still have capacity. Every decision records how it was made, how many teammates were eligible, who was chosen, and their measured load — so an unexplained or fake assignment cannot appear successful.
The Inbox in the app is where your team is told first. For an automatically assigned request, the alert goes only to that teammate, and also reaches their linked browser or phone push and personal Telegram. We only mark an alert as reaching someone once an endpoint confirms it; an in-app alert with no linked endpoint stays as accepted.
Email, a workspace Telegram and webhooks are not switched on yet (Rolling out). We do not label them delivered just because they are set up: every attempt is recorded as accepted or suppressed, so a silent gap is visible. AI Assistant adds these routes as your deployment turns them on.
## Turning it on
Human handoff is **opt-in**. It is off until you turn it on for your workspace, because it depends on your team being ready to answer. When you turn it on (in [Governance and models](https://busymate.ai/docs/governance)), the assistant starts offering a person and your Inbox goes live. Leave it off and the assistant simply never promises a person it cannot deliver.
> **Honest by design.** With handoff off, the assistant will not tell a customer "I'll connect you to someone" — it offers a person only when you have actually staffed one. That keeps the promise real.
### How does a customer ask for a person?
In their own words — "can I talk to someone". The assistant recognizes the request and raises it; the customer stays in the same conversation.
### What does the teammate see?
The full conversation the customer and the assistant already built, before they claim it. Their reply goes into that same conversation.
### Which alerts are switched on?
The Inbox in the app, plus each teammate's linked browser or phone push and personal Telegram. Email, a workspace Telegram and webhooks are Rolling out.
### How do I set it up?
Turn it on in Governance, staff the Inbox, pick how conversations are assigned — step by step in [Set up human handoff](https://busymate.ai/docs/guides/human-handoff-setup).
## Next
- **[Governance & model policy](https://busymate.ai/docs/governance)** — enable handoff and set who can answer.
- **[The assistant experience](https://busymate.ai/docs/assistant-experience)** — where the user asks for a human.
---
---
title: "Connecting your systems (MCP) | AI Assistant"
description: "Connect your own MCP server so the assistant can act on your customers' data — each customer authorizing with their own account."
last_updated: "2026-09-13T14:04:10+03:00"
---
# Connecting your systems (MCP) | AI Assistant
Source: https://busymate.ai/docs/connectors
Last modified: 2026-09-13T14:04:10+03:00
MCP (the Model Context Protocol) is an open standard for giving an AI assistant tools. AI Assistant lets your mate use your tools through your own MCP server, on behalf of your customers, under rules you set. Each tool gets an access level — open to anyone, signed-in customers, or on the customer's behalf — and every change you mark waits for a confirmation card. Your server learns who the customer is from a signed token it verifies, and returns only that customer's data.
If your systems already speak MCP, your mate connects to them the same way it connects to anyone's — no platform-specific glue.
## How your mate acts on your customers' data
When your assistant needs to do something, it calls a tool on your MCP server. Two things make that safe:
1. **Your mate tells your server who the customer is** — as a short-lived, cryptographically **signed** token your server verifies. It can't be faked or replayed, so your server always knows exactly which of your customers a request is for.
2. **Your server enforces the scope.** Because your MCP server knows the customer, it returns and changes only *that* customer's data. Your mate never sees more than your server hands it.
Diagram: A customer chats with your mate; your mate calls your MCP server carrying a signed token identifying the customer; your server verifies it and returns only that customer's data
## Access levels
Every tool you expose gets an access level, so the assistant can only reach what's appropriate for who's asking:
| Access level | Who it's for | What it allows |
|---|---|---|
| **Open to anyone** | Anyone, including visitors | Safe, non-personal look-ups — product info, general help. |
| **Signed-in customers** | A signed-in customer | Look-ups and actions on **their own** data only. |
| **On the customer's behalf** | A signed-in customer, for actions that need their say-so | The same, for actions you want the customer to authorize. |
| **Confirmation step** | Any change you mark | The customer must confirm before it runs — the full action is shown first. |
Changes that matter are held behind a **confirmation step**: Your mate shows exactly what it's about to do and waits for a yes. Nothing changes silently.
## Setting it up
The **Integration** section in your AI Assistant Console is the source of truth for this setup. It is generated from the selected workspace's live settings, so its URLs, sign-in values, snippets, and release checklist stay current — there is no separate handoff document to keep in sync.
1. Open [Console → Integration](https://busymate.ai/console/integration) and select the workspace you are configuring.
2. Follow its MCP step to **Connections**, add your server URL and how it authenticates, then run the probe.
3. Review every tool it finds and set each one's access level and confirmation deliberately.
4. Run the checks and publish. A failed probe or an incomplete sign-in setup blocks publishing, instead of producing a half-connected assistant.
The Integration section also gives you a copy-ready brief and the AI Assistant management MCP address for teams that want an AI agent to do the same setup. See [Getting started](https://busymate.ai/docs/getting-started).
### Connect Claude Code to the management MCP
One command; the browser handles sign-in with your normal account (OAuth 2.1 — nothing is pasted):
```bash
claude mcp add --transport http busymate-ai https://busymate.ai/mcp
```
Then run `/mcp` inside Claude Code and choose **busymate-ai → Authenticate**. Any other HTTP-MCP client (Claude Desktop, Cursor, …) connects with just the URL `https://busymate.ai/mcp` — sign-in is discovered automatically, so no other configuration is needed.
## Customers connecting their own accounts
**Each customer can connect their own account** to a service you do not run. Use it when a customer must authorize their own account elsewhere. Set the connection's authorization details in **Connections**, then publish its on-the-customer's-behalf tools.
There are two ways the assistant can act for a signed-in customer:
- **Automatic (a signed action proof)** — recommended when your product has already verified the visitor. AI Assistant creates a short-lived, workspace-and-connection-bound proof for your MCP server, so account access is automatic and the customer does not see a second sign-in or consent prompt.
- **Each customer signs in once (OAuth)** — use this when a separate consent is intentional. The assistant shows **Authorize account tools** once, uses OAuth 2.1 with PKCE, and binds the grant to the workspace, the connection, and the verified customer.
Both fail closed. Your MCP server works out the customer only from the verified token, and never trusts an account id passed in a tool's arguments. OAuth grants can be revoked from the account menu; signed proofs expire in at most five minutes and are pinned to one connection.
### Does my server have to speak MCP?
Yes — standard MCP over HTTPS, JSON-RPC 2.0, with schemas on `tools/list`. No platform-specific glue.
### How does my server know which customer is asking?
From the verified token your mate sends: a short-lived signed proof, or a per-customer OAuth token. Never from an account id in a tool's arguments.
### Automatic proof or per-customer OAuth?
The signed proof when your product already verified the visitor and you want no second consent. OAuth when a separate consent screen is intentional.
### Where do I set it up?
Console → Connections, then run the checks and publish. The steps, the probe and a worked call are in [Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server).
## Next
- **[Getting started](https://busymate.ai/docs/getting-started)** — where your MCP server is connected.
- **[Governance and models](https://busymate.ai/docs/governance)** — turn actions on and set confirmation rules.
- **[The assistant experience](https://busymate.ai/docs/assistant-experience)** — how actions appear to a customer.
---
---
title: "Managing from chat | AI Assistant"
description: "Manage your workspace by chatting — ask the assistant to change branding, features and limits, within your role."
last_updated: "2026-09-04T13:14:52+03:00"
---
# Managing from chat | AI Assistant
Source: https://busymate.ai/docs/managing-from-chat
Last modified: 2026-09-04T13:14:52+03:00
You can manage your AI Assistant workspace in the Console, or ask the signed-in assistant to do the same work in chat. Both use the same AI Assistant management tools (MCP at `https://busymate.ai/mcp`). Look-ups run right away; changes show you exactly what will change and wait for your yes; and every call stays within the workspaces your account may manage.
## Use the Console
Every control this section describes lives in the visual **Console** scoped to your workspace:
- **Branding** — your name, logo, colors, and welcome copy.
- **Governance** — model choice (customers pick, or one default), your allowed models, and limits. See [Governance and models](https://busymate.ai/docs/governance).
- **Connections** — the tools your assistant can use. See [Connecting your systems](https://busymate.ai/docs/connectors).
- **Usage** — how much your assistant is used, per AI provider and over time. See [Usage and analytics](https://busymate.ai/docs/usage).
- **Handoff** — turn human support on and manage the Inbox. See [Human handoff](https://busymate.ai/docs/human-handoff).
Each panel is point-and-click, changes are reviewed before they publish, and everything you can touch is your workspace's — never anyone else's.
## Scoped by who you are
Management is always **role-scoped**, whether from the Console or from chat:
Diagram: Three levels: the platform team manages all workspaces; a workspace admin manages only their own workspace; a guest gets guidance only, no management
- A **workspace admin** (you) manages your own workspace — your branding, your rules, your usage. A change to anyone else's workspace is structurally impossible for you.
- The **platform team** (us) manages workspaces across the platform — creating one, verifying a domain.
- A **guest** gets no management at all — just help understanding what the assistant does.
Your role comes from your verified sign-in, never from anything typed in a chat, so the boundary holds no matter what's asked.
## Manage by chatting
Conversational management is shipped. Ask things such as *"Show this workspace's integration status,"* *"List the content, skills, and plugins I can publish,"* *"Add this MCP connection,"* or *"Review the last 30 days of conversations."* The assistant uses the same workspace-scoped management tools the Console uses.
Inside AI Assistant, a verified workspace admin or a member of the platform team gets the first-party `platform-management` connector automatically. There is no second MCP setup step in the Console and no shared application token: every call rides the signed-in person's own grant to the management address above.
The contract is deliberately simple:
- **Look-ups run right away.** For example, `get_tenant_integration`, `list_tenant_resources`, `list_tenant_conversations`, and `list_tenant_insights` return current workspace state.
- **Changes show their payload and wait for approval.** For example, `upsert_tenant_connector` and `publish_tenant_runtime` do nothing until the signed-in admin confirms the exact action.
- **A workspace admin is held to their active workspace.** A workspace id put in a prompt cannot cross that boundary, and membership is checked again when the tool runs.
- **The platform team uses its verified role.** Guests get guidance only and are never given management tools.
Older integrations may still call support-era names such as `upsert_tenant_support_connector` and `list_support_insights`. They still work as compatibility names, but new clients should use the product-neutral names.
## Connect from another MCP client
External MCP clients connect directly to `https://busymate.ai/mcp` with OAuth 2.1 (Claude Code: `claude mcp add --transport http busymate-ai https://busymate.ai/mcp`, then `/mcp` → Authenticate). The account you connect with decides which workspaces and actions are available; putting a workspace id in a prompt or a tool argument never grants access. Keep confirmation on for changes.
### Can a workspace admin change another workspace?
No. A workspace admin is held to their active workspace; a workspace id in a prompt or a tool argument never crosses that boundary.
### Are changes confirmed?
Yes. A change shows its exact payload and does nothing until the signed-in admin confirms it. Look-ups run right away.
### Which MCP clients work?
Any HTTP-MCP client — Claude Code, Claude Desktop, Cursor — with the URL `https://busymate.ai/mcp`. Sign-in is discovered at the address, so no extra configuration is needed.
### Is the developer-tools MCP the same server?
No. The management MCP serves this product only; the developer-tools product has its own server, and neither borrows tools or credentials from the other.
## Next
- **[Governance and models](https://busymate.ai/docs/governance)** — inspect or change the same rules from the Console or chat.
- **[Usage and analytics](https://busymate.ai/docs/usage)** — inspect workspace usage in the Console or ask for it.
- **[Getting started](https://busymate.ai/docs/getting-started)** — set up your workspace first.
---
---
title: "Glossary | AI Assistant"
description: "The AI Assistant vocabulary — each term in plain words first, then its technical name, linked to the guide that uses it."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Glossary | AI Assistant
Source: https://busymate.ai/docs/glossary
Last modified: 2026-09-19T18:23:03+03:00
The words these docs use, defined once. Each entry gives the plain phrase first and the technical name in parentheses, then links to the guide that uses it. Generic standards get one line and a link to the specification. Every developer page follows the same convention at the first mention of a term.
## Workspace (tenant)
Your own space on the platform, kept separate from everyone else's: name, slug, branding, web addresses, sign-in provider, connections, content, model rules and limits. Everything your customers do stays inside it. In the API and tool names it is called a tenant.
See: [Getting started](https://busymate.ai/docs/getting-started)
## Your address on our domain (default host)
The `.busymate.ai` address every workspace answers at from the moment it exists, before any DNS work.
See: [Serve your assistant on your own domain](https://busymate.ai/docs/guides/custom-domain)
## Your own domain (white-label host)
Your own web address, mapped to your workspace after you prove you own it (a TXT record) and point it at us (a CNAME). Your customers see your domain only.
See: [Serve your assistant on your own domain](https://busymate.ai/docs/guides/custom-domain)
## Signed-in customer (identified launch)
Opening the assistant as a known customer: the widget or app hands over a proof your product signed, and your mate serves that customer's history and account tools.
See: [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)
## Sign-in proof (launch token)
A short-lived signed proof (a JWT, ES256 by default, at most 120 seconds) your API creates for one signed-in customer: issuer, audience busymate-ai, workspace claim, unchanging subject, nonce, one-time jti.
See: [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)
## One-time values (nonce and jti)
The two values on a sign-in proof that can be used once. The widget generates the nonce and your endpoint echoes it; the jti is the proof's own id. Each pair is consumed exactly once, so a replay is refused.
See: [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)
## Your sign-in provider (identity provider)
What the platform checks sign-in proofs against: your issuer, public-key URL (JWKS), audience, workspace and subject claims, allowed algorithms, maximum proof age and the endpoint that creates proofs.
See: [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)
## Access levels (tool tiers)
The three levels a tool can have: open to anyone (public — non-personal look-ups), signed-in customers (identified — their own data), on the customer's behalf (delegated — for changes you want explicitly authorized).
See: [Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server)
## Confirmation step (confirm gate)
The per-tool flag that stops a change at a card showing the exact action; it runs only after the customer says yes. Not a fourth level — a step on any level's tool.
See: [Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server)
## Action proof (signed actor token)
The pass your mate sends your MCP server when your product already verified the visitor: signed by the platform, valid at most five minutes, issuer https://busymate.ai, audience your origin, workspace and connection pinned. No second consent prompt.
See: [Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server)
## Each customer connects their own account (per-user OAuth)
The alternative: each customer authorizes their own account with your authorization server through OAuth 2.1 with PKCE. Use it when a separate consent screen is intentional.
See: [Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server)
## Connection (connector)
A registered MCP server on your workspace: address, transport, how it authenticates, how it learns who the customer is, and the access level and confirmation flag of every tool it exposes.
See: [Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server)
## Published version (revision)
One frozen, published set of your settings. Draft, then checks (preflight), then publish, then live. A failed check leaves the draft a draft. "Live" (projection) is the copy the assistant serves; published and live are reported separately.
See: [Getting started](https://busymate.ai/docs/getting-started)
## Handoff (intervention)
A conversation moving from the assistant to your team, with the whole chat attached. Raised when a customer asks for a person, when a connected system requires it (a refund above your limit), or when one of your rules decides. In tool names it is an intervention.
See: [Set up human handoff](https://busymate.ai/docs/guides/human-handoff-setup)
## Who can open a shared page (artifact visibility)
Only you (private), your team (internal), or anyone with the link (public — listed in the gallery and the sitemap).
See: [Share pages the assistant makes](https://busymate.ai/docs/guides/artifacts)
## Allowed websites (embed origin and launch origin)
An embed origin is a website allowed to show the chat launcher; a launch origin is a website allowed to open the full-page chat with a sign-in proof. Both are settings on your workspace.
See: [Serve your assistant on your own domain](https://busymate.ai/docs/guides/custom-domain)
## Resolution (Shopify plans)
A shopper conversation the assistant handled on its own, without your team — billed once per conversation, and only then. A handoff to your team, an "I'm not sure" reply, or a conversation nobody answers is never billed. The unit the Shopify plans count.
See: [AI support assistant for your Shopify store](https://busymate.ai/docs/guides/shopify)
## MCP (Model Context Protocol)
The open standard for giving an AI assistant tools over HTTPS. Specification: modelcontextprotocol.io.
## JSON-RPC 2.0
The request-and-response format MCP uses. Specification: jsonrpc.org/specification.
## OAuth 2.1 with PKCE
The sign-in flow the management MCP and per-customer connections use; PKCE is the check that ties the returned code to the app that started the flow. Specification: the OAuth 2.1 draft and RFC 7636.
## JWT and JWKS
A JWT is the signed set of claims a sign-in proof is; JWKS is the public keys it is checked against. Specifications: RFC 7519 and RFC 7517.
---
---
title: "Guides | AI Assistant"
description: "Step-by-step guides for AI Assistant: Shopify, connecting your systems, human handoff, in-app support for iOS and Android, your own domain, your content, signed-in customers and shared pages."
last_updated: "2026-09-25T21:39:44+03:00"
---
# Guides | AI Assistant
Source: https://busymate.ai/docs/guides
Last modified: 2026-09-25T21:39:44+03:00
Step-by-step guides for AI Assistant: Shopify, connecting your systems, human handoff, in-app support for iOS and Android, your own domain, your content, signed-in customers and shared pages.
## On this page's section
- [Install spotlight knowledge search | AI Assistant](https://busymate.ai/docs/guides/knowledge-widget.md): Add keyboard-first documentation search with your existing knowledge index, configurable triggers, and cited answers.
- [AI support assistant for your Shopify store | AI Assistant](https://busymate.ai/docs/guides/shopify.md): Add AI Assistant to your storefront with no theme edit, let it learn your products and policies, and give signed-in shoppers answers about their orders.
- [AI support assistant for your WooCommerce store | AI Assistant](https://busymate.ai/docs/guides/woocommerce.md): Create a read key in your store admin, let your mate learn the catalogue and policies, and give a signed-in customer answers about their own orders.
- [Install the real WordPress plugin (not just a script tag) | AI Assistant](https://busymate.ai/docs/guides/wordpress.md): Install the AI Assistant plugin, add the floating widget or an inline [bmai_assistant] block, publish your knowledge, and recognize signed-in visitors.
- [Add your mate to a Ghost publication | AI Assistant](https://busymate.ai/docs/guides/ghost.md): Wire the embed through Ghost's own Code Injection, let it learn your posts and pages, register a member as your identified reader, and add an inline placement.
- [Add the assistant to your Webflow site | AI Assistant](https://busymate.ai/docs/guides/webflow.md): Paste the embed script where your plan allows it, or use the Designer Extension to insert it as a real page element — then teach it your content and recognize signed-in visitors.
- [Add your mate to a BigCommerce store | AI Assistant](https://busymate.ai/docs/guides/bigcommerce.md): Add the universal Script Manager embed or the native single-click app, connect an MCP server to your live catalogue and orders, and recognize signed-in customers via the Customer Login API.
- [Add your mate to a Wix site | AI Assistant](https://busymate.ai/docs/guides/wix.md): Add the embed via Custom Code on Premium or an Embed HTML element on the free plan, let it learn your pages, and recognize signed-in Members.
- [Add your mate to a Squarespace site | AI Assistant](https://busymate.ai/docs/guides/squarespace.md): Wire the embed through a Code Block/Embed Block (or Code Injection on Business+), let it learn your pages, register a member as your identified visitor, and add page actions with WebMCP.
- [Connect your MCP server as assistant tools | AI Assistant](https://busymate.ai/docs/guides/connect-mcp-server.md): Register your MCP server as your assistant's tools, give each tool an access level, mark the changes that need a confirmation card, then publish.
- [Set up human handoff | AI Assistant](https://busymate.ai/docs/guides/human-handoff-setup.md): Turn on handoff, staff the Inbox, choose how conversations are assigned, set how your team is alerted, and measure with ratings and response targets.
- [In-app AI support for iOS and Android | AI Assistant](https://busymate.ai/docs/guides/mobile-in-app-support.md): Show your assistant's chat inside your iOS or Android app, pass sign-in through your own API, and verify on a real device.
- [Serve your assistant on your own domain | AI Assistant](https://busymate.ai/docs/guides/custom-domain.md): Claim your address, prove you own it with one DNS record, point it at us with another, verify, and get a certificate automatically.
- [Teach your assistant your own content | AI Assistant](https://busymate.ai/docs/guides/knowledge.md): Let it read your help site or paste in text, publish, test what it finds, and keep every answer sourced — it says when it is not sure.
- [Let your assistant search the web | AI Assistant](https://busymate.ai/docs/guides/web-access.md): Turn on web search and page reading per workspace, choose the sites, set the limits, and test exactly what your assistant would get.
- [Recognize signed-in customers | AI Assistant](https://busymate.ai/docs/guides/identified-visitors.md): Let the assistant trust who is signed in: publish your public key, have your API sign a short-lived proof, and wire getIdentity and refreshIdentity.
- [Recognize signed-in customers in a desktop app | AI Assistant](https://busymate.ai/docs/guides/identity-desktop.md): Electron, Tauri or WebView2: mint before you navigate, put the proof in the URL fragment, then signal sign-in, sign-out and resume through the preload.
- [Recognize signed-in customers in React Native | AI Assistant](https://busymate.ai/docs/guides/identity-react-native.md): Copy one shim, inject it before content loads, and answer the same four identity messages every native bridge answers — one WebView, either platform.
- [Recognize signed-in customers in Flutter | AI Assistant](https://busymate.ai/docs/guides/identity-flutter.md): Register the BusymateAINative JavaScript channel before loadRequest, copy one Dart file, and wire sign-in, sign-out and resume — no per-platform code.
- [Sign identity tokens from your backend | AI Assistant](https://busymate.ai/docs/guides/identity-backend-signing.md): Copy the mint endpoint for Node, PHP, Python or Go, build the identical ES256 claim set, publish your JWKS, and prove it with the conformance checker.
- [The app bridge: one last update | AI Assistant](https://busymate.ai/docs/guides/app-bridge-v2.md): Install the frozen kit v2 bridge in your iOS or Android app once; every later fix ships from our side, and apps already in the stores keep working.
- [Fix a signed-in customer who shows as a guest | AI Assistant](https://busymate.ai/docs/guides/identity-troubleshooting.md): Match your exact symptom to its cause and the conformance-checker cell that proves it, including the WebView-loads-your-own-page case.
- [Follow a visitor across workspaces | AI Assistant](https://busymate.ai/docs/guides/visitor-journey.md): Platform operators: open one visitor's route across every workspace as stops, read each stop's honest identity state, and erase the visitor everywhere on request.
- [See which website tries became customers | AI Assistant](https://busymate.ai/docs/guides/preview-funnel.md): Platform operators: read every website try with its instruments, the six-stage conversion funnel, and an honest outcome per try — converted, probable, or where it left.
- [Share pages the assistant makes | AI Assistant](https://busymate.ai/docs/guides/artifacts.md): Ask your mate for a report, diagram or how-to and get a self-contained page at your address; choose who can open it, comment inline, manage it from chat.
- [Let the assistant use your page | AI Assistant](https://busymate.ai/docs/guides/page-tools.md): Publish what your page already does — look up an order, book a slot, start a return — as actions the assistant runs, asking first before changing anything.
- [Forms and sign-in inside the chat | AI Assistant](https://busymate.ai/docs/guides/form-cards.md): Ask for missing details as a card with real fields instead of a list to type, and let visitors sign in to your site without leaving the conversation.
- [Let your users sign in from the chat | AI Assistant](https://busymate.ai/docs/guides/in-chat-sign-in.md): Give customers a sign-in card inside the chat, using the accounts you already run: what they get, how to switch it on, and what stays with you.
- [How your page and the widget talk to each other | AI Assistant](https://busymate.ai/docs/guides/widget-page-api.md): The complete host ↔ widget contract: open, preset, ask and identify from the page; the events the widget posts back; page tools both ways; navigation, theming, security and a cookbook.
- [Ask without signing in: the public tools | AI Assistant](https://busymate.ai/docs/guides/public-tools.md): What anyone can ask the site assistant or the MCP server without an account — pricing, overview, docs search, status, contact — and what stays private.
- [Connect Telegram for team alerts | AI Assistant](https://busymate.ai/docs/guides/telegram.md): Create a bot, give the platform its token and webhook secret, link your own account, and get hand-off alerts you can reply to from Telegram.
- [Email channel: your support address and your own mailbox | AI Assistant](https://busymate.ai/docs/guides/email.md): Use the support address every workspace gets, or connect your own mailbox through a hosted sign-in, and let your mate answer threaded, draft, summarize, translate, forward and send invites from the Inbox.
- [Get a signed webhook on every event | AI Assistant](https://busymate.ai/docs/guides/webhooks.md): Register an endpoint, verify the signature and timestamp on every call, choose your events, and handle retries and the dead-letter state.
- [Read and reply over the REST API | AI Assistant](https://busymate.ai/docs/guides/api.md): Create a workspace API key, call the REST endpoints or the MCP server with it, list and read conversations, post an operator reply, and rotate the key.
- [Keep HubSpot current from every conversation | AI Assistant](https://busymate.ai/docs/guides/hubspot.md): Create a private app token, connect it in the Console, and let hand-offs open tickets while resolved conversations keep each customer's record up to date.
- [Turn a hand-off into a Zendesk ticket | AI Assistant](https://busymate.ai/docs/guides/zendesk.md): Give the Console your subdomain, agent email and API token, and each request for a person arrives in your queue with the thread, page and requester.
## Related
- [Getting started | AI Assistant](https://busymate.ai/docs/getting-started.md)
- [Developer guide: MCP, SDKs and sign-in | AI Assistant](https://busymate.ai/developers.md)
- [Pricing | AI Assistant](https://busymate.ai/pricing.md)
---
---
title: "Install spotlight knowledge search | AI Assistant"
description: "Add keyboard-first documentation search with your existing knowledge index, configurable triggers, and cited answers."
last_updated: "2026-09-24T08:13:54+03:00"
---
# Install spotlight knowledge search | AI Assistant
Source: https://busymate.ai/docs/guides/knowledge-widget
Last modified: 2026-09-24T08:13:54+03:00
The knowledge widget searches the **same index your chat uses**. It does not crawl or embed your site a second time. Configure it in **Console → Knowledge search**. Changes save automatically; **Undo** writes the previous settings back. A revision conflict asks you to reload instead of overwriting another editor's changes. No chat publish is needed.
## Before enabling
Review the Page index and the coverage note in Knowledge search. Select all indexed knowledge, published knowledge, or connected sources. Enabling this product makes those sources searchable by visitors. Excluded pages remain excluded by the existing retrieval reader.
Add your site's exact origin to the workspace's allowed embed origins. The knowledge widget uses that same origin list, with its own enable switch. An unknown origin cannot frame it. There is no wildcard framing policy.
## Install
Copy the snippet shown by your Console. Its hostname belongs to your workspace:
```html
```
The loader installs listeners only. It does not fetch configuration, create an iframe, read storage, or download the palette until the first trigger. An optional floating button is created locally. Recopy the snippet after changing trigger settings: fetching them before opening would violate the zero-eager-network contract.
## Triggers and JavaScript
`mod+k` means Command+K on Apple platforms and Control+K elsewhere. Custom chords use `mod`, `ctrl`, `meta`, `alt`, and `shift`, followed by one letter or digit; for example `alt+shift+p`. `/` is optional. Keyboard shortcuts are ignored while composing text or editing an input, textarea, select, contenteditable element, or textbox.
```html
```
```javascript
window.BusymateKB.open("billing");
window.BusymateKB.close();
window.BusymateKB.configure({ hotkey: "alt+k", slash: true, trigger: "floating", label: "Search" });
```
`open()` takes an optional string, the query to start with (up to 500 characters). Any other value, such as the `event` an inline handler passes, opens the palette empty.
The palette opens centred over the page, which is dimmed and blurred behind it; at 640 px and below it is a full-height sheet. The modal and its stylesheet live in a shadow root on a `` element the loader appends to your page, so your site's CSS resets and `dialog` rules never reach it and its rules never reach your page. With nothing typed it offers **Actions** (Search the docs, Ask your mate), **Go to** (your site's top indexed pages, shallowest first) and **Recent searches**, whose last row is **Clear recent searches**. Typing shows results grouped by page with snippets, the cited answer as a row of its own group when enabled (Enter opens its source), and **Ask your mate about this** as the last row; until the first results arrive, Enter keeps searching rather than handing off. Use ↑/↓ to move, Tab and Shift+Tab to jump between groups, Enter to run the highlighted row (a result opens in a new tab, a Go to destination opens in the page), and Escape to close. Every row, the answer and Clear included, is an option of one listbox, so screen readers reach all of them from the search field. The native modal keeps focus inside the palette and returns it to the opener on close. Recent searches are stored locally; a browser that blocks storage still supports search.
## Results and answers
Hybrid retrieval combines lexical and semantic ranking over the existing index. Results are grouped by page: a page whose title, top heading or address names the query ranks first, a page shows at most two sections, and a copy of a page in another language gives way to the copy in the language the search names (`lang`), else to the unprefixed copy. Each row is titled by its section and shows the sentence that matched. A match by meaning alone, with none of the query's words, needs strong similarity to appear. Results without a safe HTTP(S) page URL are omitted. The optional short answer quotes an excerpt and links its source; it needs both the query's words and a close match in meaning, so weak, lexical-only or meaning-only evidence produces a refusal, not an invented answer. Search failures are distinct from zero matches.
**Ask your mate about this** opens the workspace chat with the query prefilled for review. It does not automatically send a message.
## API and MCP
The browser palette calls the tenant host:
```http
POST /api/v1/kb/search
Content-Type: application/json
Accept: text/event-stream
{"query":"billing"}
```
Anonymous scope comes from the serving host, never `tenant_id` in the body. Admission is shared across server replicas: 30 requests per visitor IP bucket per minute, and 600 per tenant per minute. No IP address or query text is stored in the events trail. A refused budget returns `429` with `Retry-After`; retrieval or configuration outages return `503`.
With `Accept: application/json`, search returns `{query, results, answer, refused, searchId}`. Each result has `id`, `title` (the section), `url` (the page, with a text fragment to the matched words where there is one), `snippet`, `group` (the page title), `score`, `pageUrl` (the page itself; group by this, since two pages can share a title), `section` (null for a page's introduction) and `highlights` (`[start, end]` offsets of the query's words in `snippet`). The score is relevance relative to the first result, which scores 1; it never increases down the list. An answer is `{text, citation}` or null. SSE sends `results`, optional `answer` chunks, then `done`. An optional `lang` in the request body (the page's language, which the loader sends for you) picks the matching copy of a page.
Workspace API keys use the existing REST/MCP facade and its `{ok, data}` response envelope. `search_knowledge`, `get_kb_widget_config`, and `set_kb_widget_config` are the corresponding MCP tools. Management calls are tenant-authorized; writes require confirmation and an expected `revision`. REST configuration is `GET /api/v1/kb/config` and `POST /api/v1/kb/config`. Never expose an API key in the install tag.
The public palette exposes `search_knowledge` as a WebMCP page tool when the platform's WebMCP provider is enabled and the browser supports it. It uses the same HTTP endpoint and limits.
## Appearance, language and measurement
The palette follows the page it opens on. Its theme is, in order: the page's own explicit choice (`data-theme="light|dark"` on ``, or a `dark` / `light` class on it, the way most site toggles pin it), then the **Appearance** setting in Console (`Light` or `Dark`; `System` means "follow the page"), then the operating system. The Console setting is read by the palette itself every time it opens, so a change in Console applies at once; nothing about appearance is written into the snippet, and there is nothing to recopy. Its language is the page's ``, then the `data-lang` on the install tag, then the visitor's browser preference. Both are read when the palette opens, so the first paint already matches, and both are re-applied without a reload if the page changes them while the palette is open: the loader watches the `` attributes and the `storage` event, so a theme toggle or a language switch on the page reaches the open palette at once. A page that sets its language in JavaScript without touching `` can pass it directly:
```javascript
window.BusymateKB.configure({ lang: "de" });
```
The palette uses the app's semantic theme tokens and tenant branding. The host page's CSS does not cross the iframe boundary.
The closed events are `kb.search.performed`, `kb.result.clicked`, `kb.zero_results`, and `kb.handoff_to_chat`. Each may carry a `surface` attribute, `marketing` or `docs`, naming which of this site's own pages opened the palette; a palette on your site never sends it. Authenticated Console previews and management MCP/API searches are excluded.
## On this site
This site runs the same widget, bound to the platform workspace and driven by its own Console settings: the **Search** field in the header (an icon on phones) and the configured shortcut open the palette on every page, including this documentation. Its **Go to** group lists this site's own navigation. Nothing beyond the loader is downloaded until you open it. **Ask your mate about this** hands the query to the chat launcher in the corner of the page, prefilled for review; **Ask your mate** with nothing typed simply opens the launcher. The metric catalogue reserves `kb_searches`, `kb_zero_result_rate`, `kb_click_through`, and `kb_handoffs`; their aggregate readers and Insights pages are phase 2, so unavailable totals are never displayed as zero.
This guide has the standard Markdown twin at `/docs/guides/knowledge-widget.md`.
---
---
title: "AI support assistant for your Shopify store | AI Assistant"
description: "Add AI Assistant to your storefront with no theme edit, let it learn your products and policies, and give signed-in shoppers answers about their orders."
last_updated: "2026-09-19T18:23:03+03:00"
---
# AI support assistant for your Shopify store | AI Assistant
Source: https://busymate.ai/docs/guides/shopify
Last modified: 2026-09-19T18:23:03+03:00
AI Assistant for Shopify is an open-source app that adds your mate, your store's own support assistant, to your storefront — no theme edit. It learns your products, policies and pages, answers shoppers in their own language, and looks up a signed-in shopper's orders. It is listed in our own app catalogue.
> **Install today:** the app is in the Shopify App Store's own review — until it's approved there, the listing's button doesn't self-serve install; it takes you to [/contact](https://busymate.ai/contact) to request access instead. Once approved, this section updates to the real Shopify install link.
## Before you start
- The **Online Store** channel and a theme that supports app embeds (Theme editor → App embeds).
- Access to the store's admin to install apps and edit the theme.
- The app is open source (MIT); the repository is linked from its listing at [/store/apps/busymate-ai-shopify](https://busymate.ai/store/apps/busymate-ai-shopify). Plans are on the listing and on [/pricing](https://busymate.ai/pricing).
## 1. Install
While the Shopify App Store review is in progress, open the listing and send a request — we'll follow up to get your store connected. Once the listing is approved and self-serve:
1. Open the listing and install the app. Shopify asks you to approve the permissions it uses.
2. The app creates your assistant and connects it to your store — nobody on our side has to do anything.
3. It sets your name and colors, lets your storefront show the chat, connects your store's orders, learns your catalog and policies, and switches the assistant on.
4. Your assistant runs at an address of its own on our domain. No DNS work; the storefront loads it through the app embed.
## 2. Turn it on
1. In the app's Home, use the theme-editor link — it opens **Online Store → Themes → Customize → App embeds** with the **AI Assistant assistant** embed ready.
2. Switch it on and click **Save**. The **Ask us** launcher appears on every storefront page.
3. Home confirms the embed is on by reading your public storefront. We can't confirm it on a password-protected store — switch it on above and the check passes once the store opens.
## 3. Check what it learned
At install — and on every reinstall, product change, or when you press **Re-train** under Store connection — the app reads your products, shop policies and pages and gives them to the assistant as its content. Home shows what it learned and when.
- Only sellable products count; draft and archived products are left out (and counted separately).
- Answers cite the page they came from. When the answer is not in your content, your mate says so instead of guessing.
## 4. Name and brand it
Open **Assistant settings** in the app. The assistant's name, colors and welcome message are yours — change them and the storefront launcher follows. Nothing here names our brand to your shoppers.
## 5. Signed-in shoppers
When a shopper is signed in, the assistant knows who they are and can check their orders. It can look up order status and tracking, and — with the shopper's confirmation — update an address, start a return, or cancel or refund an order. Larger refunds go to you: a refund above your limit is handed to your team with the request attached, never run by the assistant. Nothing changes silently — every change shows the full action first and waits for a yes.
## 6. Billing
- **No plan selected yet** — choose the $0 Free plan or a paid plan on Shopify's pricing page; Free-plan limits apply until you do.
- Plans include a monthly number of AI **resolutions** (a shopper conversation the assistant handled on its own); paid plans charge per extra resolution up to a monthly cap. The assistant is never switched off for billing.
- All charges are billed through Shopify App Pricing on your Shopify invoice. Plans and caps are on the listing and on [/pricing](https://busymate.ai/pricing).
## What the app can and can't touch
- **Permissions it asks for, and why:** your products, pages and shop policies (to learn your store), and your orders, customers, fulfilments and returns (to answer signed-in shoppers and, on confirmation, act on an order).
- **What it does not ask for:** access to your theme code; orders older than 60 days (requested separately if you need them).
- **Live vs learned:** order look-ups read Shopify live at the moment you ask; products and policies are learned at install and on re-train.
- AI answers can be wrong — the assistant answers from your content, cites its source, and says when it is not sure.
## Verify
1. Open the storefront. The launcher is visible. Ask a policy question — the reply cites the policy page.
2. Sign in as a test customer and ask "where is my order". The reply names that shopper's order only.
3. Ask for a refund above your limit. The assistant hands the conversation to your team; it appears in the Inbox.
4. Switch the storefront language. The reply follows the shopper's language.
## Technical details
For developers — the merchant flow above needs none of this. At install the app provisions one workspace per store through the AI Assistant MCP (`provision_partner_tenant`), authorized by a proof-of-shop signature, so no operator is involved. It sets branding, allows your storefront origins, registers the store's Admin API as the assistant's connection, trains on the catalog, and publishes one version. Signed-in identity rides Shopify's App Proxy: Shopify signs the request and the app mints a short-lived ES256 launch token for that shopper. The connection's tools have access levels — open to anyone (products, policies), signed-in shoppers (their orders, shipments, returns), and on the shopper's behalf with a confirmation card (update an address, start a return, cancel or refund, up to your refund limit). Content limits are the platform's: at most 40 sources, 20,000 characters each, 40,000 total; large catalogs are trimmed and Home says so.
### Does the app edit my theme?
No. It adds an app embed you switch on in the Theme editor. Uninstalling the app removes it.
### Where does the assistant run?
At an address of its own on our domain, created at install. The storefront only loads the chat. You can later serve it at your own domain — see [Serve your assistant on your own domain](https://busymate.ai/docs/guides/custom-domain).
### Can it refund an order?
Up to the refund limit you set, and only after the shopper confirms the exact action. Above that limit it hands the conversation to your team.
### What does it cost?
Plans are on the listing and on [/pricing](https://busymate.ai/pricing). Until you pick one on Shopify, the app shows "No plan selected" and the Free-plan limits apply.
## Next
- **[Teach your assistant your own content](https://busymate.ai/docs/guides/knowledge)** — what the app learns, and how to add more.
- **[Set up human handoff](https://busymate.ai/docs/guides/human-handoff-setup)** — staff the Inbox before shoppers ask for a person.
- **[Serve your assistant on your own domain](https://busymate.ai/docs/guides/custom-domain)** — move from the default address to yours.
---
---
title: "AI support assistant for your WooCommerce store | AI Assistant"
description: "Create a read key in your store admin, let your mate learn the catalogue and policies, and give a signed-in customer answers about their own orders."
last_updated: "2026-09-17T13:48:07+03:00"
---
# AI support assistant for your WooCommerce store | AI Assistant
Source: https://busymate.ai/docs/guides/woocommerce
Last modified: 2026-09-17T13:48:07+03:00
AI Assistant reaches a WooCommerce store through the store's own REST API: the catalogue, categories, shipping zones and policy pages become content your mate answers from, and a signed-in customer can ask where their order is. Nothing is installed inside WooCommerce for that — the connection is a read key you create in the store admin.
Every step below is proven against a real WooCommerce store, end to end, by a shopper in the widget.
## Before you start
- A WooCommerce store on HTTPS with **Settings → Permalinks** set to anything but **Plain**: the REST routes live under `/wp-json/wc/v3/`, and plain permalinks do not serve them.
- An admin account on the store, to create the key.
- Your workspace open in the Console.
## 1. Create a read key
1. In the store admin, open **WooCommerce → Settings → Advanced → REST API** and choose **Add key**.
2. Describe it so you recognize it later, pick the user it acts as, and set **Permissions** to **Read**.
3. Generate it. The consumer key (`ck_…`) and consumer secret (`cs_…`) are shown once — copy both before leaving that page.
Read is enough for everything here: a write key would let the assistant change orders and move money, so the connection does not ask for one.
## 2. Connect the store
Open [Console → Knowledge base](https://busymate.ai/console/knowledge) and add the store with three values: the store address, the consumer key and the consumer secret. The secret is stored value-blind — afterwards the Console shows a hint, never the value.
It is verified before anything is saved: one call to `GET /wp-json/wc/v3/system_status` proves the site really runs WooCommerce, the credential authenticates, and the key reads more than a public resource. A failure is reported as a sentence, never saved as a row claiming to be connected.
Over MCP: `connect_commerce`, `get_commerce_status` and `sync_commerce`, each taking a `kind` of `woocommerce`.
## 3. Choose what it reads
Four corpora, and you pick the set:
- **Products** — name, price, stock state and description, each cited to its own permalink.
- **Categories** — the category archive a shopper can open.
- **Shipping** — the store's shipping zones.
- **Policies** — your WordPress pages for shipping, returns, refunds, privacy and terms, so the answer and the page a customer is pointed at stay the same text.
Products are bounded by a page limit you set, 1 to 50, default 20. It is the same knowledge pipeline as a website source or pasted text — see [Teach your assistant your own content](https://busymate.ai/docs/guides/knowledge).
## 4. Let it re-read the store
The store is re-synced on a schedule you set — daily by default, hourly at most. A sync compares the newest edit time across products and pages against the last one seen; when nothing changed it is skipped and not one memory is rewritten. **Sync now** in the Console, or `sync_commerce`, forces a pass.
## 5. Order lookup for a signed-in customer
Order lookup is identity-gated: it answers only with the signed-in customer's own orders, and has no anonymous arm.
The match is made on **your store's own customer id**. The plugin signs the WordPress user id into the proof — exactly the customer a WooCommerce order carries — so the store filters on it directly, and no email or phone number crosses the browser. A storefront that signs customers in another way falls back to a verified email.
Ownership is checked **again** on every order returned: a filter you asked for is not a filter that was applied. An order number alone is a guessable integer, never sufficient.
What comes back: order number, status, the dates placed, paid and completed, the total, the shipping method, the items, and a tracking number when the store recorded one.
## 6. Put the chat on the storefront
One WordPress plugin does both jobs: it loads the chat on every storefront page and signs a short-lived proof of who is logged in, so order lookup needs no second sign-in. Your customer database is never shared.
1. Download the plugin from the store connection card in your Console; the zip is built for your workspace, so there is nothing to type into it.
2. In wp-admin, open **Plugins → Add New → Upload Plugin**, upload the zip and activate it.
3. Its status screen lists the values for **Console → Identity** and has a **Test identity** button that proves the signing endpoint answers.
Without the plugin the chat still works — add the embed script to your theme — but the storefront must then sign identity itself: [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors).
## Returns are answered, not started
The connection deliberately offers no `start_return` action. WooCommerce core has no customer-initiated return resource in its REST API, and `POST /orders//refunds` is an admin refund that moves a merchant's money — not something to run on a visitor's say-so. Returns are a plugin choice that differs per store.
So the assistant answers the returns **policy** from your own returns page, with a citation, and hands the conversation to a person for the rest.
## Manage from any MCP client
Every step above is available from any MCP client on your own account:
```bash
claude mcp add --transport http busymate-ai https://busymate.ai/mcp
```
## Troubleshooting
- **401** — the key was refused: check the consumer key and secret, and that the key has **Read** permission.
- **404** — the address answers but the REST API is not there: WooCommerce is not active, or **Settings → Permalinks** is still **Plain**.
- **Unreachable** — the address is wrong or the store is down. The connect reports the status it saw.
## Verify
1. The Console card shows the store name, the WooCommerce version and the currency read at connect.
2. Ask about a product only your catalogue knows. The reply cites the product's own page.
3. Ask about your returns policy. The reply cites the returns page and offers a person rather than starting a return.
4. Sign a test customer in and ask where their order is: the reply names that customer's orders only. Ask signed out and the assistant asks them to sign in.
### Does the key need Write permission?
No. Read covers the catalogue, the policies and order lookup. Write access is never asked for, because everything it would unlock moves a merchant's money.
### Can a shopper see somebody else's order?
No. The lookup runs only for a signed-in customer, is scoped to that customer's own key, and re-checks ownership on every row before showing it. There is no search-all-orders path.
### How often does it re-read my catalogue?
On the schedule you set — daily by default, hourly at most. An unchanged store is skipped, so re-syncing a quiet catalogue costs nothing.
### Do I need the plugin?
Only to hand over who is signed in. The chat can go on the site with the embed script; the plugin saves you signing the proof by hand.
## Next
- **[Teach your assistant your own content](https://busymate.ai/docs/guides/knowledge)** — help pages beside the catalogue.
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — the proof order lookup relies on.
- **[Set up human handoff](https://busymate.ai/docs/guides/human-handoff-setup)** — staff the Inbox before the returns questions.
---
---
title: "Install the real WordPress plugin (not just a script tag) | AI Assistant"
description: "Install the AI Assistant plugin, add the floating widget or an inline [bmai_assistant] block, publish your knowledge, and recognize signed-in visitors."
last_updated: "2026-09-19T18:23:03+03:00"
---
# Install the real WordPress plugin (not just a script tag) | AI Assistant
Source: https://busymate.ai/docs/guides/wordpress
Last modified: 2026-09-19T18:23:03+03:00
AI Assistant reaches WordPress through a real, installable plugin — not a copy-pasted script tag. The plugin loads the floating widget on every front-end page and signs a short-lived proof of who is logged in, so a returning signed-in visitor never has to sign in twice.
This is the WordPress-specific path. Wix, Squarespace and Webflow don't yet have a native app of their own — on those builders you still add the one script tag from [Add AI Assistant to WordPress, Wix or Squarespace](https://busymate.ai/articles/add-ai-assistant-wordpress-wix-squarespace).
## Before you start
- A self-hosted WordPress site (wordpress.org). The widget works on any PHP build; signed-in visitor recognition additionally needs the OpenSSL PHP extension (ES256) — without it the widget still loads, and the settings screen names exactly what your host is missing.
- An admin account on the site.
- Your workspace open in the Console.
## 1. Download your plugin
Every workspace's plugin is generated for that workspace — your workspace id and the embed origin are baked in at download time, so there is nothing to type into it.
1. In your [Console](https://busymate.ai/console), open your connection settings and choose **Download WordPress plugin**.
2. In wp-admin, open **Plugins → Add New → Upload Plugin**, upload the zip and activate it.
3. Activation generates the site's own ES256 signing key. Its settings screen shows the values a signed-in-visitor identity provider needs (issuer, JWKS URL, the launch endpoint) and a **Test identity** button that proves the signing endpoint answers.
The plugin's own copy, look and available tools all come from AI Assistant at chat time — updating any of that never means updating the plugin.
## 2. Put the chat where you want it
Out of the box the plugin adds the floating widget to every page — nothing else to do. To put a button inside a specific page (a contact page, a pricing page) that opens the same assistant — optionally with a starting question — use the **Busymate AI trigger** block in the block editor, or the `[bmai_assistant]` shortcode anywhere shortcodes work:
```
[bmai_assistant label="Ask about sizing" prompt="What sizes do you have?"]
```
Both ship directly in the plugin you downloaded — no separate install, no iframe to hand-place. See the live example at [wordpress.demo.busymate.ai](https://wordpress.demo.busymate.ai) (Larkspur Studio), where both the floating widget and an inline block are visible.
**Always give the button a `prompt`.** Without one it opens the chat on an empty composer and the visitor has to think of something to type — the whole point of an in-page button is that the assistant is already answering by the time they look at it. With a `prompt` the question is sent for them, exactly as if they had typed it.
### If the open chat covers your page
Some themes run edge to edge — a full-width hero, a headline or a column that reaches the right margin — and the open chat panel then sits on top of it on a wide screen. Under **Settings → Busymate AI → Widget → On wide screens**, switch from *Float the chat over the page* to *Make room for the chat beside the page*: your pages shift left only while the chat is open, and only above 1100 pixels. Phones and tablets, where the chat is full-screen anyway, never move.
If your theme styles `padding` on the `` element itself, move that to a wrapper first — in this mode the widget owns it. The reserved width is also published as the CSS variable `--bmai-chat-reserve` on ``, alongside `data-bmai-chat="open"`, if you want to shift something the padding cannot reach.
## 3. Teach it your content
Point a [website source](https://busymate.ai/docs/guides/knowledge) at your site, or paste in your own pages and policies — the same knowledge pipeline every connection uses, with citations back to the page an answer came from.
## 4. Recognize signed-in visitors
The plugin signs the WordPress user id into its proof, so a signed-in visitor is recognized automatically once you register it as an identity provider — see [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors) for the exact fields (issuer, JWKS URL, the launch endpoint the plugin's settings screen shows) and how to verify the wiring end to end before trusting it with real customer data.
## Agent-ready out of the box
The plugin makes your site readable, understandable and operable by AI agents and answer engines — not just by people — with nothing to configure:
- **`/llms.txt`** — a short, standards-shaped index of your site (name, key pages, latest content, contact) that an AI assistant reads before answering a question about you.
- **`/agents.json`** — the one machine-readable capability card an agent looks for first: your content index, the MCP interface your connected workspace exposes (once you've connected one — see [Manage from any MCP client](#manage-from-any-mcp-client) below), and a human contact.
- **Markdown on request, on the SAME page** — request any published page or post with `Accept: text/markdown` and you get that exact page back as clean Markdown with a small frontmatter (title, description, canonical URL, last-updated date, language) instead of HTML — never a separate `/…/md` URL to keep in sync. Ordinary visitors and search engines keep seeing HTML.
- **Structured data + discovery headers** — JSON-LD on every page, and `Link:` headers pointing at `/llms.txt` and `/agents.json` on every response, so a crawler that only reads headers still finds them.
- **Your site's own WordPress abilities become browser tools automatically** — if your WordPress install exposes the core Abilities API (WordPress 6.9+), a signed-in visitor's own abilities are bridged into the same browser-native tool surface (WebMCP) the plugin's page tools already use — no second list to maintain, nothing to turn on.
Every one of these is *generated* from your site's own published content and connected workspace — there is no template to fall out of date, and each updates automatically the moment you save a post. See the live example at [wordpress.demo.busymate.ai/llms.txt](https://wordpress.demo.busymate.ai/llms.txt) and [/agents.json](https://wordpress.demo.busymate.ai/agents.json).
## Manage from any MCP client
Every step above is available from any MCP client on your own account:
```bash
claude mcp add --transport http busymate-ai https://busymate.ai/mcp
```
## Troubleshooting
- **Settings screen warns about openssl** — the site's PHP build is missing the OpenSSL extension with EC (prime256v1) support; ask your host to enable it. The widget itself still works; only signed-in visitor recognition is unavailable until then.
- **The widget never appears** — check the browser console for a blocked or 404'd request to `/embed/v1.js`; a caching plugin that strips query-string-free `
```
3. Save. The floating widget now appears on every page and post — nothing else to configure for that part.
See the live example at [ghost.demo.busymate.ai](https://ghost.demo.busymate.ai) (The Meridian Line).
## 2. Put the chat inline on a page
Out of the box you get the floating widget. For an inline placement on a specific page (an About or Contact page), add an **HTML card** in Ghost's editor with the same frame the widget opens, placed in the page flow instead of a corner button:
```html
```
The live demo shows both placements at once — the floating widget and an inline card on its About page.
## 3. Teach it your content
Point a [website source](https://busymate.ai/docs/guides/knowledge) at your own Ghost URL — the same crawler every connection uses, reading your published pages and posts and citing back to the page an answer came from.
## 4. Recognize signed-in members
Ghost's native **Members** feature is the identified-visitor layer: register your own site (or a small backend you control) as an identity provider — see [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors) for the exact fields (issuer, JWKS URL, the launch endpoint) — then define `window.BusymateAI.getIdentity` in Code Injection **before** the embed `
```
3. Set it to load on the storefront, footer placement. The floating widget now appears on every storefront page — nothing else to configure for that part. This same script can also be created through the Admin API's Content Scripts resource (`POST /v3/content/scripts`) if you are scripting the install.
See the reference build at [bigcommerce.demo.busymate.ai](https://bigcommerce.demo.busymate.ai) (Copperfield Kitchen Co.) — a real trial store's live catalogue and order book, read straight from the Admin API. The store itself is a preview, not yet launched on BigCommerce; the assistant, the catalogue read and the order lookup are real either way.
## 2. Or install the native app (single-click)
For a one-click install instead of a pasted script, a BigCommerce app (registered on the [developer portal](https://devtools.bigcommerce.com)) can install the same loader via the Scripts API on install, and add a "Chat with your mate" widget through the Widgets API in Page Builder. This is the same OAuth single-click pattern every BigCommerce app uses — see [Connect an MCP server](https://busymate.ai/docs/guides/connect-mcp-server) for the credential shape once installed.
## 3. Teach it your catalogue
Connect an MCP server reading your store's own Admin API v3 (products, categories) and orders — see [Connect an MCP server](https://busymate.ai/docs/guides/connect-mcp-server). A store-level API account (Products + Orders read scope is enough for grounded answers) is all it needs; nothing is hardcoded, every answer reflects your live stock and prices.
## 4. Recognize signed-in customers
BigCommerce's **Customer Login API** identifies who is shopping. Register your own storefront (or a small backend you control) as an identity provider — see [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors) for the exact fields (issuer, JWKS URL, the launch endpoint) — then define `window.BusymateAI.getIdentity` in your Script Manager script **before** the embed `
```
3. Set it to load on **All pages**, placement **Body — end**. Save, then **Publish**.
4. Open the **published** page (not the Editor preview) in a private window and confirm the chat bubble appears.
**On a free Wix site**, install the AI Assistant app instead of pasting anything: in your site dashboard open **Manage apps → AI Assistant → Open in Editor**, click **Add to Site** in the panel that opens, then **Save** and **Publish**. The widget lands on your homepage and the chat bubble appears for real visitors — verified on a free `wixsite.com` site, where `busymate.ai/embed/v1.js` loads and the bubble renders. Upgrading to Premium later adds the every-page Custom Code route with no other change.
## 2. Teach it your content
Point a [website source](https://busymate.ai/docs/guides/knowledge) at your own Wix URL — the same crawler every connection uses, reading your published pages and citing back to the page an answer came from.
## 3. Recognize signed-in visitors
Wix's **Members Area** is a client-side feature (Velo's `wix-members` API), so identity has to be bridged from your site's own code rather than a server-side plugin: define `window.BusymateAI.getIdentity` (see [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors) for the exact shape) inside a Velo page/site code file, **before** the embed script runs, reading the signed-in member from `wix-members` client-side.
## Manage from any MCP client
Every step above is available from any MCP client on your own account:
```bash
claude mcp add --transport http busymate-ai https://busymate.ai/mcp
```
## Troubleshooting
- **A pasted script tag never appears on a free site** — expected, and not fixable from your side: a free site reports `shouldLoadAllExternalScripts: false` in its page source and never fetches the loader (check with `performance.getEntriesByType('resource').filter(r => r.name.includes('busymate'))` on the published page — empty, however the tag was added). Use the app's site widget on a free plan, or Custom Code on Premium; both load unconditionally.
- **It works in the Editor preview but not on the published page** — always verify on the actual published URL; the Editor's preview runtime can differ from what a real visitor's browser does with a lazy-loaded element.
- **A signed-in member is still treated as a guest** — `getIdentity` must be defined and resolved before the embed script tag runs; test it with `window.BusymateAI.getIdentity()` in the browser console.
## Verify
1. Open your published site in a private window: the chat bubble appears and answers from your own pages, with citations.
2. If you wired identity, sign in as a Wix Member and ask something only a signed-in visitor should see — the reply recognizes that visitor.
3. Ask it to hand off to a person — the conversation reaches your Inbox.
### Do I need Wix Premium?
No. A free `wixsite.com` site blocks scripts you paste yourself (`shouldLoadAllExternalScripts: false` — Embed HTML, Custom Element and Velo alike), but it runs an installed app's own widget, which is how AI Assistant gets on a free site. Premium adds the Custom Code paste that covers every page at once.
### Why does the widget sometimes take a moment to appear?
The loader is `async` and the widget mounts after the page settles, so give a published page a second before deciding it isn't there. If it never appears on a free site, check you added it as the app's site widget rather than as a pasted script — see above.
### Can I recognize a signed-in Wix Member?
Yes, by bridging `wix-members` to `window.BusymateAI.getIdentity` in your site's Velo code — see [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors).
## Next
- **[Teach your assistant your own content](https://busymate.ai/docs/guides/knowledge)** — your Wix pages, crawled and cited.
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — the `getIdentity` bridge for Wix Members.
- **[Set up human handoff](https://busymate.ai/docs/guides/human-handoff-setup)** — staff the Inbox before visitors start asking to talk to someone.
---
---
title: "Add your mate to a Squarespace site | AI Assistant"
description: "Wire the embed through a Code Block/Embed Block (or Code Injection on Business+), let it learn your pages, register a member as your identified visitor, and add page actions with WebMCP."
last_updated: "2026-09-18T11:37:06+03:00"
---
# Add your mate to a Squarespace site | AI Assistant
Source: https://busymate.ai/docs/guides/squarespace
Last modified: 2026-09-18T11:37:06+03:00
Squarespace has no plugin runtime, so AI Assistant reaches a Squarespace site through the universal embed — one script tag placed via a **Code Block** or **Embed Block** inside a page. Squarespace's own **Code Injection** (a site-wide header/footer script, the simplest path on WordPress/Ghost/BigCommerce) is a **paid-plan feature on Squarespace** — it is greyed out behind an upgrade prompt on the free trial and on the entry Personal plan, so start with the block-based path below if you're not sure which plan you're on.
## Before you start
- A Squarespace site with editor access.
- Know your plan: **Business plan or higher** unlocks Code Injection (Settings → Advanced → Code Injection). Below that, a Code Block or Embed Block on each page is the free-tier path.
- Your site's **Site Availability** (Settings → Advanced → Developer Tools → Website Protection, or Settings → Website → Site Availability) must be Public for the embed to render for visitors — Password Protected or Private sites keep the whole page (and the embed with it) behind a gate. **On a 14-day trial, "Public" is itself paid-plan-gated** ("Upgrade to publish") — only Password Protected, Private, or (Enterprise) SSO Protected are selectable until the site is on a paid plan, independent of whether Code Injection is unlocked. Building the demo/knowledge/connector pieces below does not require publishing; a real visitor reaching the widget does.
- Your workspace open in the [Console](https://busymate.ai/console).
## 1. Add the embed
**If your plan has Code Injection (Business or higher):**
1. Open **Settings → Advanced → Code Injection**.
2. Paste the one script tag from your Console connection settings into **Header**:
```html
```
3. Save. The floating widget now appears on every page — nothing else to configure for that part.
**On a plan without Code Injection:**
1. Open the page you want the widget on (or repeat this on every page) in the Squarespace editor.
2. Add a **Code Block** (or an **Embed Block**, which wraps the same idea) and paste the same script tag.
3. Save and **publish the page** — a block's script only runs on the published site, not in the editor preview.
Either way, the widget needs your site to be **Public** to render for a real visitor — see "Before you start" above.
## 2. Register the page's own actions (WebMCP)
Alongside the embed script, a second small script can register the page's own actions — "view the class schedule," "book a session" — through the standard `document.modelContext` WebMCP surface, so the assistant can act instead of only answering. Drop it in that same block (or Code Injection panel), right after the embed script tag. See [Add page actions with WebMCP](https://busymate.ai/docs/guides/page-tools) for the shape.
## 3. Teach it your content
Point a [website source](https://busymate.ai/docs/guides/knowledge) at your own Squarespace site URL — the same crawler every connection uses, reading your published pages and citing back to the page an answer came from.
## 4. Recognize signed-in customers
Squarespace's native **Member Areas** feature (on Business/Commerce plans) is the identified-visitor layer: register your own site (or a small backend you control) as an identity provider — see [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors) for the exact fields (issuer, JWKS URL, the launch endpoint) — then define `window.BusymateAI.getIdentity` **before** the embed `
```
## Search and canonical
Keep one primary address. Your workspace's search setting decides whether the address is indexable; its `sitemap.xml` and `robots.txt` are generated from your settings.
## Remove
Domains → Remove (or `remove_tenant_custom_domain`) unmaps the address and cleans up. Your address on our domain keeps serving.
## Troubleshooting
- **A 200 on `/` is not proof.** Until the address is mapped and published, signed-in paths answer 403 with `unknown_host`. Test a signed-in path, not the landing page.
- **TXT not found** — check the exact record name (`_busymate-support.` plus your address) and wait for the change to spread.
- **CNAME conflict** — an A, AAAA or TXT record at the same name blocks the CNAME at most providers.
## Verify
1. `https://ai.yourdomain.com/` renders your brand, not the plain shell.
2. A signed-in path at the new address no longer answers 403.
3. `https://ai.yourdomain.com/sitemap.xml` lists your address.
### Do I need DNS for the address on our domain?
No. `.busymate.ai` is live from the start. DNS is only for your own domain.
### Who sets up the certificate?
We do, automatically, once verification succeeds. There is nothing to upload or renew.
### Can I keep both addresses?
Yes. Mark one primary; both answer with your brand. Links and the sitemap follow the primary one.
### Why does my address answer 403?
It is not mapped yet, or the version that maps it is not published. Verify the domain and publish; the 403 disappears.
## Next
- **[Getting started](https://busymate.ai/docs/getting-started)** — workspace, web address, sign-in, systems, publish.
- **[A white-label AI assistant on your own domain](https://busymate.ai/platform/white-label)** — why the domain is the product.
- **[White-label AI for agencies and resellers](https://busymate.ai/solutions/agencies-white-label)** — one address per client.
---
---
title: "Teach your assistant your own content | AI Assistant"
description: "Let it read your help site or paste in text, publish, test what it finds, and keep every answer sourced — it says when it is not sure."
last_updated: "2026-09-12T01:26:39+03:00"
---
# Teach your assistant your own content | AI Assistant
Source: https://busymate.ai/docs/guides/knowledge
Last modified: 2026-09-12T01:26:39+03:00
AI Assistant makes your mate answer from your own content: point it at a website you run, or paste in text you wrote, then publish. Every answer is sourced, and your mate admits when it cannot be sure.
## 1. Add a website source
Point the assistant at your help site. The crawler is robots-aware, stays on the same origin and runs asynchronously; you set how many pages it may index, from 1 to 50 (the default is 20).
- Console: [Console → Knowledge](https://busymate.ai/console/knowledge) → **Add source** → URL and page limit.
- MCP: `add_tenant_knowledge_source` with `url` and `max_pages`; then `list_tenant_knowledge_sources`, `reindex_tenant_knowledge_source`, `set_tenant_knowledge_source_enabled` and `delete_tenant_knowledge_source` to manage it.
A source shows its status, the pages and chunks indexed, the last index time and the last error, so an empty crawl is visible rather than silent.
## 2. Or publish curated text
Publish text you wrote — facts, how-tos, references, glossary entries — as `knowledge_sources` on the revision itself (`publish_tenant_runtime`). Each source has a caller-stable `key`, a `label`, a `kind` (`fact`, `howto`, `reference` or `glossary`) and its `content`.
- Limits: at most 20,000 characters per source, 40 sources, 40,000 characters in total. An over-limit payload is refused before anything is written.
- Re-publishing the same source replaces it.
The Shopify app uses exactly this path when it learns your products, policies and pages.
## 3. Publish to go live
Content reaches your mate through a published version: select the sources, run the checks, publish. Published versions are frozen, so what your customers see only ever moves forward to a complete setup. See [Getting started](https://busymate.ai/docs/getting-started).
## 4. Test what it finds
- Ask your mate a question only your content can answer. The reply cites the source.
- Over MCP, `search_tenant_knowledge` returns the matching excerpt with where it came from, so you can check what the assistant would read before a customer does.
- Ask something your content does not cover. Your mate says it is not sure and, if handoff is on, offers a person.
## Starter suggestions
The welcome screen's suggestion pills are generated from your published content. Publish good sources and the first prompts your customers see are already about your product.
## The wording rule
Say it the way it works: the assistant **answers only from your content, with sources, and admits when it cannot be sure**. Claim nothing beyond that sentence.
## Manage from any MCP client
Every step above is available from any MCP client connected with your own account:
```bash
claude mcp add --transport http busymate-ai https://busymate.ai/mcp
```
## Technical details
For developers: curated text is stored in 2,000-character parts, and every answer drawn from a source carries a `[K:xxxx]` citation the reader can follow back to the exact part.
## Verify
1. A content-only question returns a cited answer.
2. An out-of-scope question is declined; with handoff on, a person is offered.
3. Change a page, reindex the source, ask again — the answer follows the change.
### How much content can I add?
Per revision: up to 40 curated sources of 20,000 characters each, 40,000 characters in total. A website source indexes up to 50 pages per source.
### Does it crawl my whole site?
No. One origin, the page limit you set, and robots rules respected. Add several sources for several sections.
### Can it make things up?
It answers from your content, with sources, and admits when it cannot be sure. Publish what you want it to know; keep the rest out.
### Do I have to re-publish after a crawl?
A website source is indexed on its own schedule; curated text is part of the revision. Reindex when your pages change; publish when you change what is selected.
## Next
- **[Governance and models](https://busymate.ai/docs/governance)** — the features and limits around the assistant.
- **[AI customer support with human handoff](https://busymate.ai/solutions/customer-support)** — grounded answers plus a person.
- **[AI sales and onboarding assistant](https://busymate.ai/solutions/sales-onboarding)** — pre-sales answers from your own docs.
---
---
title: "Let your assistant search the web | AI Assistant"
description: "Turn on web search and page reading per workspace, choose the sites, set the limits, and test exactly what your assistant would get."
last_updated: "2026-09-23T16:47:50+03:00"
---
# Let your assistant search the web | AI Assistant
Source: https://busymate.ai/docs/guides/web-access
Last modified: 2026-09-23T16:47:50+03:00
AI Assistant lets your mate look things up on the web when your own knowledge does not have the answer — today's news, a price, a product's specification — by **searching the web** and **reading a page**. It always tries your knowledge first, says which it used, and links the page when it answers from the web.
Everything here lives in **Console → Web access**. Every control saves itself and is live the moment the page says **Saved**; **Undo** writes the previous values back. There is no draft and nothing to publish.
## 1. Switch it on
Web access is **off** for a new workspace: nothing reaches the web until an admin turns on **Web access**. Under it, **Search the web** and **Read a page** can each be turned off on their own — for example, read the pages your customers paste in, but never search.
The page opens with one plain sentence about what your mate can do **right now**, read from the saved settings rather than from anything still being typed, and where those settings come from. A workspace that switched on live web search in an earlier version of its settings starts with **Search the web** on and **Read a page** off, and the page says so; the first change you make here takes over from that older setting.
## 2. Choose the sites
- **Only these sites** — when the list is empty, any public site may be used. When it has entries, only those sites and their subdomains are searched or read.
- **Never these sites** — always refused, even when the same site is also allowed.
Enter a site the way you would say it: `example.com` or `*.example.com`. A full address such as `https://example.com/page` is refused with a note, because the lists match on the site, not on a path. Both lists apply to searching and to reading a page — including every address a link redirects to, so a short link cannot lead your mate to a site you blocked. A site you block inside one you allow (allow `example.com`, block `forum.example.com`) is also left out of search results.
## 3. Set the limits
Searches and page reads count together.
| Limit | What it bounds | Range |
|---|---|---|
| Per conversation | Lookups one conversation may make | 1 – 50 |
| Per day | Lookups the whole workspace may make in a UTC day | 1 – 100,000 |
A lookup counts when it is **attempted**, so a site that keeps failing is not a free retry loop. When a limit is reached, your mate tells the person plainly that it cannot look anything else up — it never pretends it searched.
## 4. Decide how it answers
- **Cite sources** (on by default) — your mate names and links the page it used, never cites a page it did not open, and never invents an address.
- **Safe search** (on by default) — asks the search to leave out explicit results.
## 5. Try it
The **Try it** box at the bottom of the page runs one real search or one real page read under the **saved** settings and shows exactly what your mate would get: the answer with its sources, or the reason it would refuse. Tests do not count toward your limits and are not recorded as usage; they have a small allowance of their own, six every ten minutes.
## What is never read
Page reading refuses, before a single byte is fetched: addresses that are not `http` or `https`, addresses that carry a user name or password, addresses that name a port, and anything that is not on the public internet — local, private and cloud-metadata addresses, including a public name that resolves to a private one and a redirect that lands on one. A site that asks assistants not to read a page (`robots.txt`) is respected. The text of a page is bounded, and your mate is told when it was cut.
## Manage from any MCP client
The same settings are available to agents and scripts: `get_tenant_web_access`, `set_tenant_web_access` and `test_tenant_web_access` over MCP, or `GET /api/v1/web-access`, `POST /api/v1/web-access` and `POST /api/v1/web-access/test` over the [REST API](https://busymate.ai/docs/guides/api). A change names only the fields to change — every other field is kept — and is live when it returns.
The Insights metrics `web_searches` and `web_fetches` count lookups per day. Each lookup is also recorded in the events trail with its site and outcome, never the question or the page text.
## Verify
1. In **Try it**, search for something your knowledge does not cover — the answer arrives with its sources.
2. Add that answer's site to **Never these sites** and run the same test — it is refused with the blocked-site reason.
3. Turn **Web access** off and ask your mate the question in the chat — it says it cannot look this up here and answers from your knowledge.
### Does my mate search the web for every question?
No. It answers from your own knowledge first and goes to the web only when that knowledge does not have the answer, or when the question is about something that changes, such as news or prices. It says which one it used.
### Does it cost me more when it searches?
Each lookup is bounded by the per-conversation and daily limits you set, so you decide the ceiling. Tests from the Try-it box never count toward them.
### Can it read pages behind a login or on my private network?
No. It only reads public addresses. Private, local and cloud-metadata addresses are refused before anything is fetched, and so is any address carrying a user name or password.
### Can I allow only my own sites?
Yes. Add them to **Only these sites** and both searching and page reading stay on those sites and their subdomains.
## Next
- **[Teach your assistant your own content](https://busymate.ai/docs/guides/knowledge)** — the knowledge your mate tries first.
- **[REST API](https://busymate.ai/docs/guides/api)** — keys, scopes and the rest of the workspace API.
---
---
title: "Recognize signed-in customers | AI Assistant"
description: "Let the assistant trust who is signed in: publish your public key, have your API sign a short-lived proof, and wire getIdentity and refreshIdentity."
last_updated: "2026-09-23T16:47:50+03:00"
---
# Recognize signed-in customers | AI Assistant
Source: https://busymate.ai/docs/guides/identified-visitors
Last modified: 2026-09-23T16:47:50+03:00
AI Assistant recognizes your signed-in customers without sharing an account database. Your API signs a short-lived proof for each one (a launch token — an ES256 JWT valid for 120 seconds with a one-time nonce), you publish the matching public key (JWKS) as the workspace's identity provider, and the widget calls `getIdentity`, `identityChanged`, and `signedOut`. Your mate then serves that customer's history and account tools — only theirs.
## Two identities
- **Your team** signs in to the Console with their own accounts.
- **Your customers** never get a platform account. Each launch carries a short-lived proof your product signed; its subject is your unchanging customer id. The claims prove who is chatting.
## 1. Keys
Create an ES256 key pair. Publish the public key at `https://yourdomain/.well-known/jwks.json` with a `kid`; keep the private key on your server. ES256 is the reference algorithm; the allowed list is part of the registration.
## 2. Register the provider
Open [Console → Identity](https://busymate.ai/console/identity) — or call `upsert_tenant_identity_provider` — and enter:
| Field | Value |
|---|---|
| Issuer | your origin, for example `https://yourdomain` |
| JWKS URL | `https://yourdomain/.well-known/jwks.json` |
| Audience | `busymate-ai` |
| Workspace claim | `tenant_id`, equal to your workspace id |
| Subject claim | `sub` — the unchanging internal customer id |
| Max proof age | at most 120 seconds |
| Mint endpoint | the URL of the endpoint from step 3 |
Save the draft, run the checks, publish. An incomplete sign-in setup blocks the release.
## 3. The mint endpoint
Your API exposes one endpoint requiring your own signed-in session, returning a freshly signed proof for that customer:
```typescript
import { SignJWT, importJWK } from "jose";
// POST https://YOUR-PRODUCT-DOMAIN/api/bmai/identity — requires YOUR OWN logged-in product session.
app.post("/api/bmai/identity", requireSession, async (req, res) => {
// Fresh on EVERY mint. Never persist the launch token/nonce in localStorage,
// sessionStorage, cookies, React state, or module state: every assistant
// launch consumes this pair exactly once.
const nonce = typeof req.body?.nonce === "string" ? req.body.nonce : "";
if (!/^[A-Za-z0-9_-]{32,200}$/.test(nonce)) return res.status(400).json({ error: "invalid_nonce" });
const key = await importJWK(JSON.parse(process.env.AI_LAUNCH_PRIVATE_JWK), "ES256");
const token = await new SignJWT({
tenant_id: process.env.BMAI_TENANT_ID, // the tenant id shown in Console → Identity
nonce, // equals the sibling field below
name: req.user.displayName, // optional low-sensitivity display claim
})
.setProtectedHeader({ alg: "ES256", kid: process.env.AI_LAUNCH_KEY_ID })
.setIssuer(process.env.BMAI_ISSUER) // the Issuer you registered in Console → Identity
.setAudience("busymate-ai")
.setSubject(req.user.id) // IMMUTABLE internal account id; never email/phone/session id
.setJti(crypto.randomUUID()) // one-time (replay-protected)
.setIssuedAt()
.setExpirationTime("120s") // <= registered max age (120s)
.sign(key);
res.set("Cache-Control", "no-store");
res.status(201).json({ token, nonce, expiresIn: 120 });
});
```
- The nonce arrives from the widget, must match `^[A-Za-z0-9_-]{32,200}$`, and is echoed back.
- Claims: `iss`, `aud`, `sub`, your workspace claim, `nonce`, a one-time `jti`, `iat`, `exp` within the registered max age.
- Respond `201` with `{ token, nonce, expiresIn }` and `Cache-Control: no-store`. Each pair is consumed exactly once.
## 4. Wire the widget
Define `window.BusymateAI.getIdentity` before the embed script loads. It returns a fresh `{ token, nonce }` for a signed-in customer, `null` for a signed-out one. Call `identityChanged()` after login, token rotation and every account switch — it upgrades the live conversation in place, same thread. Call `signedOut()` on logout — it ends the session and clears the previous transcript. (`refreshIdentity()` still answers, as an older, heavier fallback that remounts instead of upgrading in place.) Never keep a token or nonce in storage, cookies or state. Console → Integration renders the full embed snippet with your values.
## 5. Pick your platform
Every platform is the same four messages over a different transport. The
integration kit at `https://busymate.ai/sdk/v1/kit/` has a drop-in for each one,
a runnable sample beside it, and a README that names the four obligations in one
place.
| You are building | Copy | Signal in with | Signal out with | Guide |
|---|---|---|---|---|
| A website, any framework | `kit/web/busymate-identity.js` | `identityChanged()` | `signedOut()` | this page |
| Android (WebView) | `sdk/v1/android/BusymateAI.kt` | `bridge.identityChanged()` | `bridge.signedOut()` | [In-app support](https://busymate.ai/docs/guides/mobile-in-app-support) |
| iOS (WKWebView) | `sdk/v1/ios/BusymateAI.swift` | `bridge.identityChanged()` | `bridge.signedOut()` | [In-app support](https://busymate.ai/docs/guides/mobile-in-app-support) |
| Electron · Tauri · WebView2 | `kit/desktop/preload.js` | `busymateDesktop.identityChanged()` | `busymateDesktop.signedOut()` | [Desktop](https://busymate.ai/docs/guides/identity-desktop) |
| React Native | `kit/react-native/busymateIdentity.js` | `bridge.identityChanged()` | `bridge.signedOut()` | [React Native](https://busymate.ai/docs/guides/identity-react-native) |
| Flutter | `kit/flutter/busymate_identity.dart` | `bridge.identityChanged()` | `bridge.signedOut()` | [Flutter](https://busymate.ai/docs/guides/identity-flutter) |
| Your API | `kit/backend/{node,php,python,go}` | — | — | [Backend signing](https://busymate.ai/docs/guides/identity-backend-signing) |
Each platform guide ends with the same conformance checker this page uses, run against your own integration rather than a sample.
**Your app's WebView loading your OWN page is a fifth shape, not a variant of the above.** If your app loads a page you built — which then embeds the assistant — the assistant asks that page, not your app; your page has no session of its own, so only the first launch is ever answered. See [Fix a signed-in customer who shows as a guest](https://busymate.ai/docs/guides/identity-troubleshooting) for the fix.
Three rules apply on every platform above, and missing any is silent:
1. **Register the bridge before the page loads.** On the web the loader parks an
ask it cannot answer and flushes it later; in an app, a handler installed
after first paint depends on a bounded retry that eventually gives up.
2. **Mint fresh on every ask.** A launch proof is single-use. A cached one is
refused, which is why this failure reads as intermittent rather than broken.
3. **Signal both directions.** A sign-in that is never signalled leaves a
signed-in customer talking as a guest; a sign-out that is never signalled
leaves their conversation in front of whoever is next at that device.
You never configure cookies for any of this. Identity arrives from you on each
launch, so third-party cookie policy has no bearing on whether a customer is
recognized.
## 6. Full-page open
For a hosted page instead of an embed, mint the same pair and open your address with the token and nonce in the URL **fragment** — never the query string, referrer or logs. The destination strips it before the exchange. The hosted-handoff snippet in Integration shows the exact form.
### The redirect handoff your own login page must complete
Registering `loginUrl` is a *second* way to identify a visitor: when a signed-out visitor clicks **Sign in** on your standalone hosted page, the platform sends them to your `loginUrl` with `return_to` and a one-time `bmai_nonce` on the query string. Your login page must, on that same request:
1. Complete (or already hold) your customer's normal sign-in.
2. Mint the pair with the identical endpoint your `identityEndpointUrl` uses, passing the received `bmai_nonce`.
3. Redirect the browser to the **exact** `return_to` value, with `#bmai_token=&bmai_nonce=` in the URL fragment — never the query string.
A page that ignores `return_to`/`bmai_nonce` — a plain login form that only redirects to your dashboard — signs the customer in for real while the chat receives no token and stays a guest, which from their side is indistinguishable from a broken sign-in. The platform cannot finish it for you: minting the token needs YOUR signing key. Console → Integration → Identity flags this as soon as `loginUrl` is registered and no `hosted_web` identified session has ever been recorded.
## 7. Sign in inside the chat
The redirect above takes the visitor away and brings them back. A `sign_in` tool
— a page tool, or one on your connected server — does it without leaving the
conversation: your own form is rendered in the thread as you returned it, the
credential reaches your backend and never the assistant, and on
`{ signedIn: true }` the widget calls your `getIdentity` again and re-mints the
session **in place** — the same thread, now identified. Implement it and that is
what a signed-out visitor gets; implement nothing and they get the redirect
above, which is why registering `loginUrl` matters even when you plan to add the
tool later. See [Let your users sign in from the chat](https://busymate.ai/docs/guides/in-chat-sign-in) for
the tool, and [Forms and sign-in inside the chat](https://busymate.ai/docs/guides/form-cards) for the
card contract.
## 8. Sign in as a test account (machine sign-in)
Your mate can run the full diagnostics as one of your own customers — Console → Connections → Test account, or `verify_tenant_private_tools` — but only if it can sign in *as* that account without a browser. The endpoint in step 3 cannot do that: by design it requires the customer's own session, and a check is a server with no session and no origin. This section is the contract that closes the gap.
**You publish nothing secret and we hold nothing of yours.** The check presents a short-lived signed assertion; you verify it against our published keys with any standard JOSE library, and answer with the **same launch token** your step-3 endpoint already mints. There is no shared secret to install, store or rotate.
### What we send
The check POSTs to the same `identityEndpointUrl`, with an `Authorization: Bearer` assertion instead of a customer session:
| | |
|---|---|
| Format | JWT (RFC 7519), signed **ES256** (RFC 7518), `kid` in the header |
| Our keys | `https://busymate.ai/.well-known/jwks.json` (RFC 7517) |
| `iss` | `https://busymate.ai` |
| `aud` | **your identity provider's issuer origin** — an assertion minted for you is inert everywhere else |
| `sub` | the **login** of the test account registered in your workspace |
| `purpose` | `test-account-sign-in` — this token is valid at nothing else, and nothing else is valid here |
| `tenant_id` | your registered **Workspace claim** value — the same one your own launch tokens carry |
| `nonce` | the same nonce in the body; echo it back as always |
| `jti`, `iat`, `exp` | one-time id; at most **60 seconds** old |
### ⚠️ The one rule that matters
A valid assertion means *"busymate.ai is asking, on behalf of the account named in `sub`"*. It does **not** mean *"mint for whoever `sub` says"*. Check `sub` against your own list of review logins and refuse anything else. A tenant that mints for an arbitrary `sub` has handed us the ability to impersonate its customers — which is exactly what the launch-token design exists to prevent.
### Add it to your endpoint
```typescript
// Add this ONE branch to the endpoint from step 3. Everything else there
// stays exactly as it is: your customers' browsers keep using the session arm.
import { createRemoteJWKSet, jwtVerify } from "jose";
// Our published keys. Cache the key set — do not fetch it per request.
const BUSYMATE_JWKS = createRemoteJWKSet(
new URL("https://busymate.ai/.well-known/jwks.json"),
);
// The review logins YOUR product recognises. THIS LIST IS THE SECURITY BOUNDARY:
// a valid assertion means "busymate.ai is asking on behalf of ", never
// "mint for whoever says". Anything not in here must be refused, or you
// have handed us the ability to impersonate your customers.
const REVIEW_LOGINS = new Set(["bmaireview@your-assistant.example"]);
app.post("/api/bmai/identity", async (req, res) => {
const auth = req.headers.authorization ?? "";
if (auth.startsWith("Bearer ")) {
let claims;
try {
({ payload: claims } = await jwtVerify(auth.slice(7), BUSYMATE_JWKS, {
issuer: "https://busymate.ai",
// YOUR provider's issuer origin. An assertion minted for someone else
// fails here, which is what audience binding is for.
audience: "https://YOUR-PRODUCT-DOMAIN",
algorithms: ["ES256"],
maxTokenAge: "60s",
}));
} catch {
return res.status(401).json({ signedIn: false, reason: "invalid_assertion" });
}
// Single-purpose: this token is valid for nothing else, and nothing else
// is valid here.
if (claims.purpose !== "test-account-sign-in") {
return res.status(403).json({ signedIn: false, reason: "wrong_purpose" });
}
if (typeof claims.sub !== "string" || !REVIEW_LOGINS.has(claims.sub)) {
return res.status(403).json({ signedIn: false, reason: "not_a_review_account" });
}
const user = await findUserByLogin(claims.sub); // YOUR lookup
if (!user) {
return res.status(403).json({ signedIn: false, reason: "not_a_review_account" });
}
// The SAME mint your browser arm already calls, with the SAME nonce rules.
return res.json(await mintLaunchToken(user, String(claims.nonce ?? req.body.nonce)));
}
// ── unchanged: your customers' own session ──────────────────────────────
const user = await currentUser(req);
if (!user) return res.status(401).json({ signedIn: false });
return res.json(await mintLaunchToken(user, req.body.nonce));
});
```
### Verification checklist
Run these before calling machine sign-in done.
1. `GET https://busymate.ai/.well-known/jwks.json` returns our current signing key set. This is one optional arm of machine sign-in; a `404` here means that arm is not available yet, not that setup on your side is wrong — skip to "Or publish a `sign_in` tool instead" below and use that arm.
2. A request with **no** `Authorization` still behaves exactly as before for your customers' browsers. This section adds an arm; it must not change the session arm.
3. A hand-made token signed with **your own** key is **refused** — you verify against *our* JWKS, not yours.
4. An assertion whose `aud` is another origin is **refused**.
5. An assertion whose `sub` is a real customer who is *not* a registered review login is **refused**. This is the rule above; test it explicitly.
6. An assertion older than 60 seconds is **refused**.
7. A valid assertion returns the **identical** `{ token, nonce, expiresIn }` shape your browser arm returns, with the nonce echoed.
8. Console → Connections → Test account → **Run full check** reports `sign_in` green and names the method.
### Or publish a `sign_in` tool instead
If you already expose sign-in as a tool (see §7), you need none of the above: the check calls your own `sign_in` on one of your connected servers with the account's login and its password — or its **code**, if your product signs customers in with a one-time code and you have registered the account as *One-time code (fixed review code)*. Either arm is enough; the Console names the one your workspace is missing.
## 9. Acceptance checklist
Setup progress proves configuration, not that the flow works. Run this before calling sign-in done.
Start with **Console → Identity → "Test identified launch"** on your provider row. It runs the real preflight: it fetches your JWKS through the outbound guard and checks a usable key exists for **every** algorithm you registered (ES256 needs an EC P-256 key), pushes a deliberately unsigned probe carrying your own issuer, audience and claims through the **same verifier the live launch uses** — which must refuse it — and reports whether the provider is in the current draft and in the published revision. A 404 JWKS URL, a key set with no matching key, a mistyped issuer or audience, or an enabled-but-unpublished provider each show up as a red arm with the exact reason, instead of as a customer whose session quietly degrades to anonymous. The same check decides the publish gate's `identified-launch`, so a red here blocks the release.
To prove the last step end to end, mint a real token with your own endpoint and paste it with its nonce: the test verifies that exact assertion against your registered provider and reports pass/fail with the claim names it checked. It never stores or echoes the token, the subject, or any claim value. `test_tenant_identity_provider` is the MCP twin and runs the identical check.
```text
REQUIRED AUTH + HISTORY ACCEPTANCE — AI Assistant / your mate
Setup progress is configuration evidence; it does not prove this workflow.
[ ] Console -> Identity -> "Test identified launch" is GREEN on your provider row.
(It fetches your JWKS and checks a key exists for every algorithm you
registered, then pushes a deliberately unsigned probe through the same
verifier the live launch uses and requires it to be REFUSED, and confirms the
provider is in the draft AND the published revision. Do not eyeball the form.)
[ ] Signed out: getIdentity returns null and the assistant remains anonymous.
[ ] Login without reloading the host page: call identityChanged(); the frame becomes identified.
[ ] Every getIdentity call returns a different nonce AND JWT jti. No launch token or nonce is persisted.
[ ] The subject is the same immutable internal AI Assistant account id across sessions/devices — never email, phone, browser id, or session id.
[ ] Send an identified message; refresh the host page; the same conversation reappear.
[ ] That refresh produces no launch_replayed, invalid_token, or silent anonymous fallback.
[ ] Logout: call signedOut(); account data is unavailable and the frame is anonymous.
[ ] Login again as the same account: call identityChanged(); that user's identified history returns.
[ ] Switch to a second account: call identityChanged(); it cannot see the first user's history or data.
Do not rely on a post-message-only identify() flow. Use identityChanged() after login,
access-token/session rotation, and every account switch; use signedOut() after logout.
```
## 10. Check your own integration
"My customer shows up as a guest" is the one symptom every mistake on this page
produces, and it names none of them. Two checks, one set of rules: the
[Identity check](https://busymate.ai/developers/identity-check) grades the
lifecycle half against your own page (nothing uploaded, shapes recorded rather
than values), and the CLI grades the backend half a browser cannot read — JWKS,
claim shape, TTL, nonce echo, clock skew, your endpoint's origin policy. Each
exits `0` passed, `1` failed, `2` could not be observed — never a silent pass.
[Fix a signed-in customer who shows as a guest](https://busymate.ai/docs/guides/identity-troubleshooting)
walks each symptom to its cause and the cell that proves it.
Your own workspace sees the identical picture without leaving the Console:
**Customers** lists every recognized person with their platforms and last-seen
state, and **Identity health** shows the same lifecycle cells this checker
runs, per platform, with the exact missing step named against real traffic
instead of a manual probe.
## Pitfalls
- An email, phone number or session id as `sub`. Use the unchanging internal id.
- A token or nonce kept in `localStorage`, a cookie or React state.
- A proof older than the registered max age, or a missing `kid`.
- Answering `getIdentity` for a signed-out customer with anything but `null`.
## Verify
1. Signed out: the assistant is a visitor session.
2. Log in without reloading and call `identityChanged()`: the frame is identified.
3. The same customer on a second device sees the same history.
4. A second account cannot see the first's history or data.
### Do you store my customers' accounts?
No. Each launch carries a proof your product signed; the subject is your id. Conversations are keyed to that subject inside your workspace.
### Why does the proof expire in 120 seconds?
It is a launch proof, not a session. A fresh one is minted per launch and used once, so a leaked proof is useless within moments.
### Which algorithm must I use?
ES256 is the reference. The algorithms your provider accepts are part of its registration, checked against your JWKS.
### Can visitors still chat?
Yes, when guest access is on. Signed-out visitors get answers and guidance; account tools need a signed-in customer.
## Next
- **[Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server)** — the tools that need this identity.
- **[In-app AI support for iOS and Android](https://busymate.ai/docs/guides/mobile-in-app-support)** — the same proof through a native bridge.
- **[White-label SDK → Customer identity](https://busymate.ai/developers#identity)** — the full contract.
---
---
title: "Recognize signed-in customers in a desktop app | AI Assistant"
description: "Electron, Tauri or WebView2: mint before you navigate, put the proof in the URL fragment, then signal sign-in, sign-out and resume through the preload."
last_updated: "2026-09-21T07:42:22+03:00"
---
# Recognize signed-in customers in a desktop app | AI Assistant
Source: https://busymate.ai/docs/guides/identity-desktop
Last modified: 2026-09-21T07:42:22+03:00
AI Assistant treats your desktop shell as its own platform. An Electron, Tauri or WebView2 window loads the assistant as the **top-level** document — there is no parent window to ask, so first launch carries identity differently than a website or a mobile WebView does, and everything after first launch goes through a small preload instead.
## 1. Mint before you navigate, not after
With no parent to ask, first launch is served well by asking nobody: your main process mints the sign-in proof itself and puts the pair in the URL **fragment** before the window ever loads the address.
```typescript
// Full-page open with identity in the URL FRAGMENT — never sent in the
// request line, referrer, or logs; the destination strips it before exchange.
function newLaunchNonce() {
const bytes = new Uint8Array(32);
crypto.getRandomValues(bytes);
return btoa(String.fromCharCode(...bytes))
.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
}
const nonce = newLaunchNonce();
const accessToken = await getProductAccessToken(); // YOUR existing auth helper
if (!accessToken) throw new Error("Sign in before opening an identified assistant");
const returnTo = new URL(location.href); returnTo.search = ""; returnTo.hash = "";
const response = await fetch("https://YOUR-PRODUCT-DOMAIN/api/bmai/identity", {
method: "POST", credentials: "include", cache: "no-store",
headers: { "content-type": "application/json", authorization: "Bearer " + accessToken },
body: JSON.stringify({ nonce, returnTo: returnTo.href }),
});
if (response.status === 401) throw new Error("Sign in before opening an identified assistant");
if (!response.ok) throw new Error("AI identity mint failed (" + response.status + ")");
const identity = await response.json();
if (typeof identity.token !== "string" || typeof identity.nonce !== "string") {
throw new Error("AI identity mint returned an invalid response");
}
if (identity.nonce !== nonce) throw new Error("AI identity nonce mismatch");
const url = new URL("https://your-assistant.busymate.ai/");
url.hash = new URLSearchParams({
bmai_token: identity.token,
bmai_nonce: identity.nonce,
}).toString();
location.assign(url);
```
`channel=desktop` (set on the URL, shown above as part of the address) labels the session "Desktop app" everywhere it appears — the Console, the transcript, the checker.
## 2. Copy the preload
Electron: `https://busymate.ai/sdk/v1/kit/desktop/preload.js`, with `contextIsolation: true` and `nodeIntegration: false`. Your main process answers the `busymate:mint-identity` IPC channel the preload sends; a runnable main process is at `https://busymate.ai/sdk/v1/kit/desktop/sample-main.js`.
- **Tauri** — the same three calls become one `#[tauri::command] fn busymate_mint()` plus `invoke("busymate_mint")` from an init script. Tauri's own `__TAURI_INTERNALS__` global is already recognized as a shell.
- **WebView2** — `AddScriptToExecuteOnDocumentCreatedAsync` installs the identical shim; answer over `CoreWebView2.PostWebMessageAsJson`, which delivers a real object rather than a string.
## 3. Everything after first launch goes through the preload
No more handshakes: call `busymateDesktop.identityChanged()` on login, token rotation or account switch; `busymateDesktop.signedOut()` on logout; `busymateDesktop.onResume()` when the window regains focus or wakes from sleep. Each mints fresh through the same main-process endpoint as first launch — never a cached pair.
## 4. What you never have to configure
Cookies, third-party or otherwise. Identity arrives from your own main process on first launch and on every later call, so nothing in this integration depends on a cookie policy, a partition setting, or your window's session store.
## Verify
1. Launch signed in: the greeting names the right customer on the very first screen, with no visible handshake.
2. Sign out inside your app; the next customer to use the window gets a guest session, not the last one's history.
3. Minimize, wait past the token's lifetime, and restore the window: the assistant is still identified, not silently downgraded to a guest.
4. Run `node v2/scripts/identity-conformance.mjs --host --json` with `channel=desktop`; it names any obligation your shell still misses instead of a bare pass or fail.
### Why mint before the window navigates instead of asking after it loads?
A desktop window has no parent to ask, and the resume budget for an in-page ask is short. Minting in the main process and handing over the pair in the URL fragment needs no round trip inside the loaded page at all.
### Does Tauri need the exact same preload file as Electron?
No. The three calls collapse to one Rust command your init script invokes; the file it replaces is `preload.js`, not a Tauri equivalent of it.
### Why does WebView2 answer differently from Electron?
`CoreWebView2.PostWebMessageAsJson` hands the page a real object. Electron's preload does the equivalent through `contextBridge`, so both avoid re-parsing a string on the page side.
### Is `channel=desktop` required, or only cosmetic?
Set it. It labels every session "Desktop app" in the Console and the transcript, and the checker reads it to grade the right lifecycle.
## Next
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — the contract this shell implements, the claims your mint endpoint signs.
- **[Sign identity tokens from your backend](https://busymate.ai/docs/guides/identity-backend-signing)** — the endpoint your main process calls.
- **[Fix a signed-in customer who shows as a guest](https://busymate.ai/docs/guides/identity-troubleshooting)** — symptom-first fixes when a cell fails.
---
---
title: "Recognize signed-in customers in React Native | AI Assistant"
description: "Copy one shim, inject it before content loads, and answer the same four identity messages every native bridge answers — one WebView, either platform."
last_updated: "2026-09-21T07:42:22+03:00"
---
# Recognize signed-in customers in React Native | AI Assistant
Source: https://busymate.ai/docs/guides/identity-react-native
Last modified: 2026-09-21T07:42:22+03:00
AI Assistant reaches a React Native app the same way it reaches any native shell: a small bridge answers who is signed in. React Native hides both platforms' native bridges behind one `WebView` component, so the kit installs a single shim that speaks the same wire messages on either OS.
## 1. Copy the shim
Copy `https://busymate.ai/sdk/v1/kit/react-native/busymateIdentity.js` into your app. It installs the Android-shaped interface on the page and forwards every message to your own `onMessage` handler — the same four messages every platform answers.
## 2. Inject before content loads, not after
Pass the shim's source to your `WebView` as `injectedJavaScriptBeforeContentLoaded`, and forward everything it posts back to `bridge.onMessage(event.nativeEvent.data, event.nativeEvent.url)` from the component's own `onMessage` prop — the full wiring is in the runnable sample below.
Use `injectedJavaScriptBeforeContentLoaded`, never `injectedJavaScript`: the latter runs after load, so the very first identity ask can arrive before your bridge exists and gets no answer at all.
## 3. What the shim mimics underneath
On Android the underlying interface looks like this — the shim gives your page the identical shape without you writing it:
```kotlin
// SDK source: https://busymate.ai/sdk/v1/android/BusymateAI.kt
// Load https://your-assistant.busymate.ai/?channel=android in a WebView.
class AssistantBridge(private val webView: WebView) {
@JavascriptInterface
fun postMessage(raw: String) {
val message = JSONObject(raw)
if (message.optString("type") != "busymate.ai.v1.identity_request") return
lifecycleScope.launch { // mint through YOUR authenticated API client
val identity = api.mintLaunchIdentity()
val response = JSONObject()
.put("type", "busymate.ai.v1.identity")
.put("token", identity.token)
.put("nonce", identity.nonce)
webView.evaluateJavascript(
"window.postMessage(${JSONObject.quote(response.toString())}, '*')", null
)
}
}
}
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(AssistantBridge(webView), "BusymateAINative")
// SupportChatNative + support.chat.v1.* remain accepted for shipped apps.
```
Your own `identityProvider` calls your authenticated API on every ask, never a cached value; `bridge.identityChanged()` on login, rotation or account switch; `bridge.signedOut()` on logout.
## 4. Run the sample first
`https://busymate.ai/sdk/v1/kit/react-native/Sample.jsx` is a working screen: a fake sign-in toggle, the shim installed, the widget reacting to both directions. Confirm it behaves before wiring your own auth store in.
## Verify
1. Signed out, open the screen: the assistant runs an anonymous, guest session.
2. Sign in inside your app, without reloading the WebView, and call `identityChanged()`: the same thread becomes identified.
3. Background the app past the token's lifetime, then foreground it: the assistant re-asks and gets a fresh answer, not a refused stale one.
4. Sign out and reopen the screen: the previous customer's history is gone, not merely hidden.
5. Run `node v2/scripts/identity-conformance.mjs --host --json` against `channel=android`; React Native answers through the same wire shape the checker already grades.
### Do I need a separate integration for iOS builds of the same app?
No. The `WebView` component and this shim are the same on both platforms; only your native project's build configuration differs, not this bridge.
### Why can't I use `injectedJavaScript` — it is simpler to reason about?
It runs once the page has already loaded, which is after the assistant's first identity ask. A handler installed that late misses the one ask that matters most, the first one.
### Does the shim work with Expo's managed WebView?
Any component built on `react-native-webview` accepts the same two props (`injectedJavaScriptBeforeContentLoaded`, `onMessage`), so the shim asks nothing extra of your bundler or build tool.
### What if my app never installs the shim?
The assistant still runs, anonymously. A missing bridge is a supported, guest state — never a broken chat — until you wire identity in.
## Next
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — the four obligations this bridge answers.
- **[In-app AI support for iOS and Android](https://busymate.ai/docs/guides/mobile-in-app-support)** — the native bridge this shim mirrors.
- **[Fix a signed-in customer who shows as a guest](https://busymate.ai/docs/guides/identity-troubleshooting)** — symptom-first fixes when a cell fails.
---
---
title: "Recognize signed-in customers in Flutter | AI Assistant"
description: "Register the BusymateAINative JavaScript channel before loadRequest, copy one Dart file, and wire sign-in, sign-out and resume — no per-platform code."
last_updated: "2026-09-21T07:42:22+03:00"
---
# Recognize signed-in customers in Flutter | AI Assistant
Source: https://busymate.ai/docs/guides/identity-flutter
Last modified: 2026-09-21T07:42:22+03:00
AI Assistant recognizes a Flutter app through one JavaScript channel your WebView plugin already knows how to register — no bridge code to write yourself, only a channel name and a small Dart file to wire in before the page loads.
## 1. Register the channel before `loadRequest`
Name it exactly `BusymateAINative`; that name already carries the shape the assistant probes for on launch. Register it — and its compatibility twin, both named in the sample below — on your `WebViewController` before you call `loadRequest`, the same "before the page loads" rule every platform in this kit follows.
## 2. Copy the identity file
Copy `https://busymate.ai/sdk/v1/kit/flutter/busymate_identity.dart` into your app. It answers the same three messages every native bridge answers: an identity request, an open-url request for external links, and a close request for the sheet.
## 3. Wire the four obligations
Your channel handler calls your own authenticated API for a fresh `{ token, nonce }` pair on every identity request — never a cached one. Call the bridge's sign-in signal on login, token rotation and account switch; its sign-out signal on logout; and its resume signal from your widget's `didChangeAppLifecycleState` when the app returns to the foreground. A signed-out answer is `null`, never a stale pair.
## 4. Run the sample first
`https://busymate.ai/sdk/v1/kit/flutter/sample.dart` is a working screen with a fake sign-in switch and the channel already registered. Confirm it behaves — an anonymous chat, then an identified one on the fake sign-in — before you connect your own auth store.
## Verify
1. Signed out, open the screen: the assistant runs an anonymous, guest session.
2. Sign in inside your app and signal it: the same conversation becomes identified, with no reload.
3. Put the app in the background past the token's lifetime, then bring it forward: the assistant re-asks and is answered fresh, not refused as stale.
4. Sign out: the next person to open the screen gets a guest session, not the previous customer's.
5. Run `node v2/scripts/identity-conformance.mjs --host --json` for whichever native platform your build targets — the WebView underneath is still iOS or Android, and the checker grades that layer.
### Why one Dart file instead of separate iOS and Android code?
The `BusymateAINative` channel name is recognized by `webview_flutter` on both platforms' underlying WebViews, so registering it once covers the same bridge shape on either OS.
### What is the "compatibility twin" the sample also registers?
An older channel name kept for apps that shipped before this vocabulary existed; the sample registers both so neither generation of the assistant's probe goes unanswered.
### Does my Flutter app need cookies configured for this to work?
No. Identity arrives from your own channel handler on every ask, so no cookie or WebView storage setting has any bearing on whether a customer is recognized.
### Can I ship the chat before my API can mint identity yet?
Yes. Without a signed answer the assistant runs anonymously; wire the four obligations in whenever your endpoint is ready, and the channel starts answering on the next open.
## Next
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — the four obligations this channel answers.
- **[In-app AI support for iOS and Android](https://busymate.ai/docs/guides/mobile-in-app-support)** — the same wire messages from the native side.
- **[Fix a signed-in customer who shows as a guest](https://busymate.ai/docs/guides/identity-troubleshooting)** — symptom-first fixes when a cell fails.
---
---
title: "Sign identity tokens from your backend | AI Assistant"
description: "Copy the mint endpoint for Node, PHP, Python or Go, build the identical ES256 claim set, publish your JWKS, and prove it with the conformance checker."
last_updated: "2026-09-21T07:42:22+03:00"
---
# Sign identity tokens from your backend | AI Assistant
Source: https://busymate.ai/docs/guides/identity-backend-signing
Last modified: 2026-09-21T07:42:22+03:00
AI Assistant needs exactly one endpoint on your API to recognize a signed-in customer: one that requires your own session and returns a freshly signed proof. Four ready-to-copy samples build the identical claim set in Node, PHP, Python and Go, so the language you already run is never a reason to hand-roll a JWT.
## 1. Pick your language, copy one file
Node (zero dependencies): `https://busymate.ai/sdk/v1/kit/backend/node/mint.mjs`. PHP (zero dependencies): `https://busymate.ai/sdk/v1/kit/backend/php/mint.php`. Python (`cryptography`): `https://busymate.ai/sdk/v1/kit/backend/python/mint.py`. Go (standard library): `https://busymate.ai/sdk/v1/kit/backend/go/mint.go`. Each builds the same response from the same claims; only the syntax differs.
## 2. The one shape every language builds
The Node sample shows the reference shape — the others build the identical claims in their own syntax:
```typescript
import { SignJWT, importJWK } from "jose";
// POST https://YOUR-PRODUCT-DOMAIN/api/bmai/identity — requires YOUR OWN logged-in product session.
app.post("/api/bmai/identity", requireSession, async (req, res) => {
// Fresh on EVERY mint. Never persist the launch token/nonce in localStorage,
// sessionStorage, cookies, React state, or module state: every assistant
// launch consumes this pair exactly once.
const nonce = typeof req.body?.nonce === "string" ? req.body.nonce : "";
if (!/^[A-Za-z0-9_-]{32,200}$/.test(nonce)) return res.status(400).json({ error: "invalid_nonce" });
const key = await importJWK(JSON.parse(process.env.AI_LAUNCH_PRIVATE_JWK), "ES256");
const token = await new SignJWT({
tenant_id: process.env.BMAI_TENANT_ID, // the tenant id shown in Console → Identity
nonce, // equals the sibling field below
name: req.user.displayName, // optional low-sensitivity display claim
})
.setProtectedHeader({ alg: "ES256", kid: process.env.AI_LAUNCH_KEY_ID })
.setIssuer(process.env.BMAI_ISSUER) // the Issuer you registered in Console → Identity
.setAudience("busymate-ai")
.setSubject(req.user.id) // IMMUTABLE internal account id; never email/phone/session id
.setJti(crypto.randomUUID()) // one-time (replay-protected)
.setIssuedAt()
.setExpirationTime("120s") // <= registered max age (120s)
.sign(key);
res.set("Cache-Control", "no-store");
res.status(201).json({ token, nonce, expiresIn: 120 });
});
```
See [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors) for the full claim table, the never-put-in-a-token list, and the key-rotation order. The short version: `iss`, `aud`, your workspace claim and `sub` (your unchanging internal id, never an email or session id) are required on every mint; `nonce` is echoed back verbatim; `jti` is one-time; `exp - iat` is at most 120 seconds.
## 3. The one thing a hand-rolled ES256 JWT gets wrong
All four samples do the ES256 **DER → raw `r‖s`** signature conversion explicitly. Skip it and a library that expects the raw form refuses every token your endpoint mints, and the refusal reads exactly like a wrong key rather than a wrong encoding — which is why every sample spells the conversion out instead of trusting a library default.
## 4. Publish the key, never the endpoint's private half
Publish the **public** key at `/.well-known/jwks.json` with a `kid` and a short cache lifetime. If the object about to be served carries a `d` field, stop: that is the private scalar, and none of the four samples will print it for you. Rotate by publishing the new key beside the old one, waiting past your cache lifetime, switching which `kid` signs, waiting one more token lifetime, then removing the old key — never the reverse order.
## 5. Prove the endpoint, not just the file
A copied file with a wrong claim, a missing `Cache-Control: no-store`, or a clock five minutes off from real time all look identical from the outside: a customer stuck as a guest. Run the CLI (`node v2/scripts/identity-conformance.mjs --host --json`) against the live endpoint before wiring any platform to it — it checks the response your language actually produces, not the sample you started from.
## Verify
1. `POST` your endpoint your own session cookie and a fresh nonce; it returns `201` with `{ token, nonce, expiresIn }` and `Cache-Control: no-store`.
2. The same request signed out, or with no session, is refused — never a token for nobody.
3. Two calls in a row return two different `jti` values and two different signatures, never a cached pair.
4. Run the CLI against the endpoint; every claim, TTL, nonce-echo and clock-skew cell passes before any platform guide above is wired to it.
### Why does the reference sample show Node when my API is in a different language?
The claim set and response shape are identical across all four; Node is shown once as the shape, and the PHP, Python and Go files build the same result in their own syntax.
### What happens if I skip the DER-to-raw conversion?
Most ES256 verifiers, including the one this platform runs, refuse the token outright. It reads as a rejected key, not as an encoding mistake, which is why the samples handle it for you.
### Can I sign with more than one algorithm?
Yes, if you register more than one in your provider setup; every mint must use an algorithm in that registered list, checked against your published JWKS.
### Do I need a separate signing key per platform I integrate?
No. One key pair, one JWKS entry, and every platform's guide above calls the same mint endpoint your key signs.
## Next
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — the full claim contract and the key-rotation order.
- **[Fix a signed-in customer who shows as a guest](https://busymate.ai/docs/guides/identity-troubleshooting)** — symptom-first fixes when a cell fails.
- **[In-app AI support for iOS and Android](https://busymate.ai/docs/guides/mobile-in-app-support)** — the platforms that call this endpoint from a native bridge.
---
---
title: "The app bridge: one last update | AI Assistant"
description: "Install the frozen kit v2 bridge in your iOS or Android app once; every later fix ships from our side, and apps already in the stores keep working."
last_updated: "2026-09-24T08:24:53+03:00"
---
# The app bridge: one last update | AI Assistant
Source: https://busymate.ai/docs/guides/app-bridge-v2
Last modified: 2026-09-24T08:24:53+03:00
AI Assistant runs inside a mobile app through one small file: the app bridge. Kit v2 is the last version of that file you ship. After it, every fix and every new feature comes from our side, and your app never needs a new build because of us.
Why it is the last one:
- **The file in your app is a pipe.** It carries one JSON message between the chat and five fixed operations in your app: `hello`, `state`, `mint`, `open` and `action`. It holds no message names, timings, retries or fallbacks, so there is nothing in it for us to fix later.
- **Everything else is served.** The chat, the embed loader and the sign-in logic load from busymate.ai each time, so an improvement reaches your customers on their next launch.
- **New abilities are opt-in.** The bridge says what it supports when it starts, and the chat uses only what was announced. An older bridge keeps working as it is.
- **The files never change under the same address.** Everything at `https://busymate.ai/sdk/v2/2.0.0/` stays exactly as it is. A new version would get a new address, announced well in advance.
The full message contract is published beside the files, at `https://busymate.ai/sdk/v2/2.0.0/CONTRACT.md`.
## 1. Know what the bridge can do
- `hello` reports the bridge version, the platform, the WebView version, your app build and the assistant it was installed for.
- `state` says whether someone is signed in, with a scrambled account key. Your user id never leaves the device.
- `mint` asks your backend for a short-lived sign-in token. Your code gets what the chat sent (a `nonce` to sign) plus `assistant` and `origin`, which the bridge sets itself so a page can never choose them.
- `open` opens an `https` link in the system browser. Any other link is refused.
- `action` hands a named action, such as `close`, to your app. Anything you do not handle is ignored.
The bridge also tells the chat when your app returns to the foreground, and when it reloaded the chat after the system stopped the web view. Only your own chat can reach it: exactly `https://.busymate.ai` and the exact `https` origins you list. There are no wildcards, so another company's chat, a shared artifact or any other busymate.ai page opened inside your app is refused, and your backend is never asked. List only hosts that serve the chat itself.
## 2. Install it on Android
Copy `BusymateBridge.kt` from the address above into your project and check its SHA-256 against `SHA256SUMS` in the same folder. Remove the kit v1 bridge (`BusymateAIWebViewBridge`, the `BusymateAINative` and `SupportChatNative` JavaScript interfaces) and any copied `native-identity.js` or `wire.js`. Then install the bridge before the first `loadUrl`, and forward renderer crashes so the chat recovers instead of the app closing:
```kotlin
// Copy https://busymate.ai/sdk/v2/2.0.0/android/BusymateBridge.kt (check SHA256SUMS).
// Needs androidx.webkit:webkit >= 1.8 and androidx.lifecycle:lifecycle-process.
BusymateBridge.install(webView, BusymateBridge.Config(
assistant = "your-assistant",
origins = emptyList(),
account = { session.userIdOrNull }, // null when signed out
mint = { request, done -> // YOUR backend signs; never a key in the app
backend.mintBusymate(request["nonce"] as String) { token, nonce ->
done(if (token != null) BusymateBridge.MintResult.Token(token, nonce) else BusymateBridge.MintResult.Failed)
}
},
))
// In your WebViewClient: the chat recovers instead of the app closing.
override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail) =
BusymateBridge.onRenderProcessGone(view, detail)
// Optional, only faster: after sign-in, sign-out or an account switch.
BusymateBridge.accountChanged()
```
## 3. Install it on iOS
Copy `BusymateBridge.swift` (iOS 14 or later) and check its SHA-256. Remove the old `BusymateAI` and `SupportChat` message handlers. Install the bridge before the first `load`, and forward web content crashes from your navigation delegate:
```swift
// Copy https://busymate.ai/sdk/v2/2.0.0/ios/BusymateBridge.swift (iOS 14+, check SHA256SUMS).
BusymateBridge.install(on: webView, config: .init(
assistant: "your-assistant",
origins: [],
account: { Auth.shared.userId }, // nil when signed out
mint: { request in // YOUR backend signs; never a key in the app
guard let pair = try await Backend.mintBusymate(nonce: request.nonce) else { return nil }
return .init(token: pair.token, nonce: pair.nonce)
}))
// In your WKNavigationDelegate: the chat reloads and restores itself.
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
BusymateBridge.contentProcessDidTerminate(webView)
}
// Optional, only faster: after sign-in, sign-out or an account switch.
BusymateBridge.accountChanged()
```
If your app sets `WKAppBoundDomains`, add the assistant's domains to that list, or the bridge cannot be reached.
## 4. Configure your website
Keep the embed script. Tell it who is signed in on your website and how to get a fresh token from your backend, and delete any page script that talked to the app directly. `account()` is your website's own session: inside your app's WebView it returns `null`, and the chat then asks the app itself, so one web bundle serves both:
```html
```
React Native, Flutter and Electron apps use the matching file from the same folder: `react-native/busymateBridge.js` with `core/busymateBridgeCore.js`, `flutter/busymate_bridge.dart`, or `electron/preload.js` with `electron/main.js`. Each takes the same `account` and `mint` callbacks. For these, load the hosted chat page directly rather than a page that embeds it.
## 5. Keep your backend as it is
Your signer keeps producing ES256 tokens with `nonce` and `jti` that expire within 120 seconds. In the app it signs the `nonce` the bridge passes; on your website `getIdentity` receives `{ nonce }` as well, and you may sign that one or your own. See [Sign identity tokens from your backend](https://busymate.ai/docs/guides/identity-backend-signing).
For app review: the bridge asks for no new permissions and downloads no code. The chat can reach only your own sign-in token, an `https` link and close. Mention chat data shared with AI Assistant in your privacy label; the AI disclosure and the report control are shown inside the chat.
## Verify
1. Signed out, open the chat: a guest chat that answers.
2. Sign in inside your app: the same conversation shows your customer's name, with no reload.
3. Put the app in the background for a minute, then bring it back: the same conversation, and no "That chat had ended".
4. Switch accounts: the first person's conversation disappears and the second person is recognized.
5. Sign out: the chat goes back to a guest.
Every session records which bridge version it came through and whether the customer was recognized, so we can tell you how each build of your app is doing without access to your code.
## How we prove it before you see it
We keep our own reference apps, built from the exact files at the address above, and run the same cases on them that you would: first launch signed out and signed in, signing in while the chat is open, switching accounts, signing out, the app in the background and restarted, the web view crashing, a slow or silent sign-in on your side. They run on real Android phones, both loading the chat page directly and loading a website that embeds it, and in real browsers: Chromium (the engine behind Chrome), Chromium with third-party cookies blocked, and the WebKit engine Safari uses. Each case must show the right person, never "That chat had ended", and a working guest chat when nobody can be recognized. Sessions recorded from every bridge version we know is in the stores, and from apps with no bridge at all, are replayed against each change.
The run happens before every change to how the chat recognizes people goes live, again right after, and every night. A failure stops the change and alerts us; it never waits for one of your customers to find it.
### Do I have to update again later?
No. This file does not change. If a new capability ever needs something new in your app, it arrives as a new, optional version announced well in advance, and the version you shipped keeps working.
### What happens to app builds that still ship the old bridge?
They keep working. We never switch an old version off, we test every bridge version still in the stores on each update, and a customer who cannot be recognized still gets a working guest chat.
### Is my user id sent to AI Assistant?
No. The bridge sends a scrambled key made from your app id and the user id, only so the chat can tell that the person changed. The token your backend signs carries the id you choose to put in it.
---
---
title: "Fix a signed-in customer who shows as a guest | AI Assistant"
description: "Match your exact symptom to its cause and the conformance-checker cell that proves it, including the WebView-loads-your-own-page case."
last_updated: "2026-09-21T07:42:22+03:00"
---
# Fix a signed-in customer who shows as a guest | AI Assistant
Source: https://busymate.ai/docs/guides/identity-troubleshooting
Last modified: 2026-09-21T07:42:22+03:00
AI Assistant shows almost the same symptom for every identity mistake: a customer you know is signed in shows up in the chat as a guest. This page starts from what you see, names the layer that actually broke, and identifies the exact cell in the conformance checker that will confirm the fix — instead of guessing across your website, your app and your backend at once.
## 1. Match your symptom to a cause
| You see | The likely cause | What proves it |
|---|---|---|
| Signed in on the site, the chat still offers Sign in | Your bridge answered nothing on the first ask — often because it registered after the page had already loaded and asked once | The [Identity check](https://busymate.ai/developers/identity-check)'s first-launch cell |
| The first message is identified, every one after is a guest | Your app's WebView loads your OWN page rather than the assistant directly, and that page only answers the very first ask | See "Your app's WebView loads your own page" below |
| The customer signs out; the chat still shows their name | The sign-out call was never wired, so the previous session survives until the view is destroyed | The checker's logout cell |
| It works on one native platform, fails on the other | The two bridges answer at the wire level differently — one can deliver a JSON string where the other delivers a real object, and a string with no `type` field is silently dropped | Run the checker against each platform on its own |
| A launch that worked a moment ago is refused as expired, or as not-yet-valid | Your server's clock has drifted past the verifier's tolerance | The CLI's clock-skew cell reads your `iat` against server time |
| A sign-in works once, then every later ask is refused | A launch proof is single-use; something cached the pair and answered a second ask with it | The checker's token-expiry cell mints twice and expects two different answers |
## 2. When your app's WebView loads a page you built
The shape that hides an otherwise-correct integration: your app's WebView does not load the assistant directly — it loads a page you built, and that page embeds the assistant. The assistant then asks its parent, which is your own page, and your page has no session of its own; the customer is signed in *natively*, one layer further out. A token on the launch URL answers the first ask and nothing after it, so a resume, a reconnect, an expiry and a restart each quietly fall back to no session there.
The fix has two halves, and both are required: keep the native bridge from [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors) installed in the app, and give your own page the small asker that forwards later questions to it — `https://busymate.ai/sdk/v1/kit/web/native-identity.js`. Installing only the bridge leaves it correct and unreachable.
## 3. Read the exact missing step, do not guess it
Two checks, one set of rules. The [Identity check](https://busymate.ai/developers/identity-check) runs the lifecycle half against a page of yours and grades what comes back: a token that was not fresh, a sign-out that changed nothing, a bridge that answered late. The CLI — `node v2/scripts/identity-conformance.mjs --host --json` — runs the half a browser cannot reach: JWKS reachability, key and algorithm agreement, claim shape, clock skew, and the CORS rules of your own endpoint. Each prints pass, fail, or could-not-observe per cell, together with the step it is missing, rather than a bare "identity broken".
## Verify
1. Run the browser check and the CLI against the same host; note any cell that fails or comes back unobserved.
2. Fix the named step, not the whole integration — one obligation from [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors) is usually the entire gap.
3. Re-run both. A clean pass on every cell, not a green first screen, is what "fixed" means here.
4. In Console → Identity health, the same picture appears per platform — the fastest way to tell whether the fix reached every customer, not only the one browser you tested from.
### Do I need to know which layer is broken before I run the checker?
No. Point it at your host and it names the layer itself — a page, a bridge, or your signing endpoint — instead of asking you to guess between them.
### What if the CLI cannot reach my identity endpoint at all?
It reports that cell as could-not-observe, not as a pass. An unreachable endpoint is never counted as working; fix reachability, then re-run.
### The browser check and the CLI disagree — which one is right?
Neither overrides the other. The browser proves the lifecycle a real page produces; the CLI proves what a browser cannot see, your endpoint's own response. A real integration passes both.
### Does either check keep or send on a real token?
No. Both record shapes — whether two mints differ, whether a claim is present — never the token, the nonce, or a claim's value.
### My symptom is not on this page — where do I look next?
Start at [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)'s four obligations; nearly every guest-when-signed-in report traces back to one of them.
## Next
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — the contract every platform below implements.
- **[Sign identity tokens from your backend](https://busymate.ai/docs/guides/identity-backend-signing)** — the endpoint every check above talks to.
- **[In-app AI support for iOS and Android](https://busymate.ai/docs/guides/mobile-in-app-support)** — the native bridge this page's WebView case depends on.
---
---
title: "Follow a visitor across workspaces | AI Assistant"
description: "Platform operators: open one visitor's route across every workspace as stops, read each stop's honest identity state, and erase the visitor everywhere on request."
last_updated: "2026-09-15T15:43:10+03:00"
---
# Follow a visitor across workspaces | AI Assistant
Source: https://busymate.ai/docs/guides/visitor-journey
Last modified: 2026-09-15T15:43:10+03:00
A platform operator can follow one visitor across every workspace they reached on AI Assistant — as a route with stops: where they arrived first, where they came back, whether they ever signed in there, and how much they talked. It is for the platform team only; a workspace operator sees a visitor's history inside their own workspace and nothing beyond it.
## Two visitor ids, two scopes
- **Workspace visitor id (`dv1_…`)** — derived per workspace from one first-party browser token. It groups a returning browser's sessions inside ONE workspace and powers the Inbox's "Returning visitor" badge and "This visitor" filter. Two workspaces never share it.
- **Platform visitor id (`pv1_…`)** — derived from the same token under a platform-only secret, with no workspace in the derivation, so the same browser gets the same id everywhere. It exists only in the platform's own graph and is readable only by platform operators.
No canvas or device fingerprinting is involved: the only signal is the opaque token the widget keeps in the browser.
## 1. Open a journey
- **From the Inbox** — on a returning visitor's conversation, the contact panel's badge offers **Cross-tenant journey →** to platform staff. It looks the workspace visitor id up in the platform graph and opens the route.
- **From Console → Platform → Visitors** — paste a `pv1_…` or `dv1_…` id into the search field, or pick a row of the recent-touchpoints feed.
- **From your AI tools (MCP)** — `get_visitor_journey` with `pv1_id`, or `list_visitor_touchpoints` with `dv1_visitor_id` for the reverse lookup.
## 2. Read the route
Each stop is one workspace, in order of first visit, and shows:
| On the stop | Meaning |
|---|---|
| The workspace's mark | Its real icon, or a monogram when none is published |
| First seen → last seen | The hop's arrival and most recent launch |
| Sessions · conversations | Counted at read time from that workspace's own tables — the graph stores no conversation data |
| `returned ×N` | The visitor came back to this workspace N times |
| Identity | One of four honest states, below |
Identity per stop is never merged across workspaces:
- **Linked to an account** — the workspace's own resolution is unambiguous; the subject is shown.
- **Anonymous** — recognised by the browser id only; never signed in there.
- **Seen with multiple accounts** — that browser was used with more than one account in this workspace. No subject is claimed; the count of accounts is shown instead.
- **No visitor id recorded** — the hop predates durable visitor ids.
Tap a stop for its touchpoint detail: entry page, referrer, sessions, conversations, the workspace visitor id, and a link to the workspace's Tenant 360 page.
## 3. Retention
The platform graph keeps a touchpoint for a fixed window after the visitor was last seen there (the window is printed on the journey, e.g. "90 days") and prunes it on its own schedule, independent of any workspace's settings. A visitor with no stops left is dropped from the graph.
## 4. Erase a visitor (GDPR)
**Purge this visitor everywhere** deletes the platform record — the id and every hop — after you confirm the exact call. Workspace-owned data (sessions, conversations, the workspace's own visitor grouping) is not touched; redact those in the workspace. The erasure is written to the platform audit log as a digest of the id, never the id itself. Redacting a contact inside a workspace also purges that person's platform record.
From your AI tools: `purge_platform_visitor_identity` with `pv1_id` and `confirm: true`.
## Who can see what
| Reader | Sees |
|---|---|
| Workspace operator | That workspace's visitor history only (Inbox badge, filter, previous conversations) |
| Platform operator | The cross-workspace route, the touchpoint feed, the purge |
Every layer enforces the same rule: the platform tables have no workspace-scoped read policy, the functions re-check the operator role on every call, the MCP tools serve only platform operators, and the Console section exists only in the platform navigation. None of the calls accept a workspace parameter.
## Reference
- Console: `/console/platform/visitors?pv1=` · `?dv1=` · `&stop=`
- MCP: `get_visitor_journey` · `list_visitor_touchpoints` · `purge_platform_visitor_identity`
- Related: [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)
---
---
title: "See which website tries became customers | AI Assistant"
description: "Platform operators: read every website try with its instruments, the six-stage conversion funnel, and an honest outcome per try — converted, probable, or where it left."
last_updated: "2026-09-15T19:31:30+03:00"
---
# See which website tries became customers | AI Assistant
Source: https://busymate.ai/docs/guides/preview-funnel
Last modified: 2026-09-15T19:31:30+03:00
Every time someone previews your mate on their own website (the hero quick start, `/try/`), the platform keeps the try — and now answers the two questions a platform operator asks about it: **did this try become a customer, and if not, where did it leave?** Console → Platform → Previews shows every try with its instruments, a conversion funnel for the window, and one honest outcome line per try. Platform operators only; a workspace never sees another workspace's tries.
## 1. Read the funnel
The widget under the timeline is a sequence of six bars for the active range (last 7 / 30 / 90 days, or the whole retained window):
| Stage | What proves it |
|---|---|
| **Tried** | The preview session exists |
| **Reached a step** | The "Keep this assistant" flow reached at least one quick-start step |
| **Signed up** | A platform account is known for the try — the scan-id stamp of a signed-in landing, or a signed-in session owner |
| **Created tenant** | A workspace was created or joined after the try began, or the scan stamped one |
| **Published** | That workspace's runtime was published at least once |
| **Subscribed** | The account holds an active or trialing subscription, or paid an invoice |
Each bar is labelled with its count and the share of the previous bar; the table under the bars repeats the numbers, and the per-site table lists tries, converted, probable and the conversion rate per host. A bar the platform could not prove (the account facts were unreadable) is drawn outlined with a `?` — never a zero.
## 2. Read a try
Open **Every try** to list every try across hosts, newest first, or pick a site and then a session. Each row and the detail pane carry the same instruments:
- **Outcome** — `Converted → (day N)`, `Probably → `, or `Left at: · last seen `.
- **Stages** — the six chips, reached or not; an unproven chip says so.
- **Quick-start steps** — `step N of 6` with the seconds each step took, and the verdict when a step failed.
- **Identity** — the same badge model as the Inbox: `no visitor id`, `anonymous`, `returning · N earlier tries`, `linked to `, or `seen with N accounts` (a shared browser is never assigned to one account).
- **Account** — the email, when it signed up, whether it existed before the try, the subscription status.
- **Client** — country, device class, OS and browser, referrer, entry URL and the UTM campaign. No IP is stored or shown.
- **AI readiness** — the cached scorecard for the host (checks passed, the missing ones) and how many pages the scan read.
- **Links** — open the try (`/try/`), open the visitor's cross-workspace journey, open the workspace.
- **Evidence** — the named facts the verdict came from (`scan_stamp`, `session_owner`, `domain_owner`, `sibling_session`, `runtime_receipt`, `subscription` …).
## 3. Converted, probable, or left
The verdict is only as strong as the link:
- **Converted** — unambiguous. The try's scan id was stamped by a signed-in landing, or the session's own signed-in owner created or joined the workspace.
- **Probable** — a plausible but unproven link: the workspace merely owns the tried domain, the conversion was stamped on a sibling try of the same browser, or the account joined a workspace after the try without the scan stamp. It is shown as probable and counted separately; it is never called converted.
- **Left** — no workspace is linked; the row says the furthest stage and when the visitor was last seen.
## 4. Filter, export, act
- **Filters** — minimum stage reached, outcome (converted / probable / left), site, search, date range. The `Subscribed` stage filter narrows to tries with a workspace and then checks subscriptions.
- **Export CSV** — every try in the current filter with all instruments as columns (bounded to 200 rows per export).
- **Realtime** — a new try, a new quick-start step or a conversion updates the page on its own; nothing polls.
- **Actions** — mark a lead, add a note, copy the invite link (unchanged from the Previews explorer).
## From your AI tools (MCP)
- `list_platform_previews` — every try with instruments; `host`, `q`, `range_days`, `stage`, `outcome`, `limit`, `offset`.
- `get_platform_preview` — one try's full instrument set by `session_id`.
- `get_platform_preview_funnel` — the rollup for `days` (optionally one `host`).
All three are platform-operator only and refuse a workspace caller before anything runs. The account facts come from `get_platform_preview_conversion_facts`, which re-checks the operator role on every call.
## Reference
- Console: `/console/platform/previews?view=tries` · `&stage=` · `&outcome=converted|probable|left` · `&site=` · `&session=`
- Related: [Follow a visitor across workspaces](https://busymate.ai/docs/guides/visitor-journey) · [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)
---
---
title: "Share pages the assistant makes | AI Assistant"
description: "Ask your mate for a report, diagram or how-to and get a self-contained page at your address; choose who can open it, comment inline, manage it from chat."
last_updated: "2026-09-12T01:26:39+03:00"
---
# Share pages the assistant makes | AI Assistant
Source: https://busymate.ai/docs/guides/artifacts
Last modified: 2026-09-12T01:26:39+03:00
An artifact in AI Assistant is one self-contained web page your mate builds on request — a report, a diagram, a how-to — served at your assistant's address under `/artifact/`. You decide who may open it (only you, your team, or anyone with the link), comment on it inline, send a thread back to your mate for a fix, and manage everything from your AI tools (MCP).
## What an artifact is
- One web page with everything inline. External scripts, stylesheets and fetches are rejected when it publishes, so a page never breaks because a third party changed.
- Addressed by a short lowercase name (slug) that is unique inside your workspace. Platform pages live at `busymate.ai/artifact/`; your workspace's live at your address.
- Owned by whoever created it, inside your workspace. Older addresses that served pages redirect to the current one.
## 1. Create
- **In chat** — ask your mate for the page you want. It builds the page and publishes it at your address.
- **From your AI tools** — `create_artifact` with `slug`, `title`, `html`, `description`, `tags` and `visibility`. The call succeeded only if the response says `created: true`; anything else is an error to read, not a page to assume.
## 2. Set visibility
| Who can open it | Who | Listed |
|---|---|---|
| Only you (default) | You | No |
| Your team | Signed-in members of your workspace | No |
| Anyone with the link | Public | In the gallery and the sitemap |
Change it any time in [Console → Artifacts](https://busymate.ai/console/artifacts) or with `set_artifact_visibility`. A private page never leaks a title or a share card, even to a stale scraper.
## 3. Comment and iterate
Open the artifact and select text, an element or a region to start a thread anchored to it. **Send to your mate** activates the thread for the AI: Your mate replies in the thread and applies the change as one `update_artifact`. Resolve the thread when the page is right; reopen it if it is not.
## 4. Meet the quality bar
Every artifact your mate publishes is held to the same bar:
- An in-page language switcher covering all 14 locales, with right-to-left layout for Arabic.
- Diagrams as inline SVG that follows light and dark themes; never an external image.
- No horizontal scroll at 390 px wide; contrast at WCAG AA.
- Concise: illustrations and numbered steps over walls of text.
## Console and AI tools
The Console lists, previews, retitles and re-scopes pages. Your AI tools (MCP) do the same: `list_artifacts`, `get_artifact`, `update_artifact`, `set_artifact_visibility`, `delete_artifact`, plus `list_artifact_comments`, `add_artifact_comment`, `reply_artifact_comment` and `resolve_artifact_comment`. Connect a client once:
```bash
claude mcp add --transport http busymate-ai https://busymate.ai/mcp
```
## Examples
Public artifacts on the platform host: the [Inbox proof of 2026-09-02](https://busymate.ai/artifact/busymate-ai-inbox-proof-2026-09-02), the [DevTools integration](https://busymate.ai/artifact/bmdev-integration) and [Build your MCP server](https://busymate.ai/artifact/build-your-mcp-server). The gallery at [/artifact](https://busymate.ai/artifact) lists every public one.
## Verify
1. The page renders in light and dark themes with no sideways scroll on a phone.
2. Switching it to "anyone with the link" adds it to the gallery and the sitemap; switching back removes it.
3. A comment sent to your mate gets a reply and one update; resolving the thread closes it.
4. The slug URL opens at your address.
### Who can see a private page?
Only you. "Your team" opens it to your workspace's signed-in members; "anyone with the link" makes it public and listed.
### Can a page load external scripts or styles?
No. Publishing rejects external resources; everything is inline so the page cannot break later.
### How do I fix a page your mate built?
Comment on the exact spot and send the thread to your mate. It replies in the thread and applies one update.
### Where does it live?
At your address under `/artifact/`. Platform-owned pages live at `busymate.ai/artifact/`.
## Next
- **[Conversation-derived product intelligence](https://busymate.ai/solutions/product-intelligence)** — reports your mate can publish as artifacts.
- **[Managing from chat](https://busymate.ai/docs/managing-from-chat)** — the same management tools, conversationally.
- **[The gallery](https://busymate.ai/artifact)** — public artifacts, live.
---
---
title: "Let the assistant use your page | AI Assistant"
description: "Publish what your page already does — look up an order, book a slot, start a return — as actions the assistant runs, asking first before changing anything."
last_updated: "2026-09-12T04:18:56+03:00"
---
# Let the assistant use your page | AI Assistant
Source: https://busymate.ai/docs/guides/page-tools
Last modified: 2026-09-12T04:18:56+03:00
AI Assistant already knows what your business says. **Page tools** let your assistant do what your *page* does — check an order, reserve a slot, begin a return — by calling functions your site already has, in the visitor's own session. Write each action once: it reaches every visitor, over WebMCP where the browser supports it and the assistant's own bridge elsewhere, apps included.
Diagram: Your page registers actions once; the assistant reaches them via WebMCP in the browser, or its own bridge elsewhere
Your assistant is already on the page, from the embed script you added at setup. Page tools ride that same script — nothing extra to install.
## 1. Decide what the page can do
Pick three to seven things a visitor does here, noting whether each only **reads** or **changes** something.
| Reads | Changes |
|---|---|
| Look up an order, check stock, show today's slots | Book, pay, cancel, submit a form, change an address |
Name each with a verb — `check_order_status`, `start_return` — and describe it as you would to a new colleague; that description is what the assistant reads to decide when to use it.
## 2. Register them
One call, where those actions live:
```javascript
// One call, both transports: the browser's own WebMCP where it exists,
// and the assistant's bridge everywhere else (Safari, Firefox, app WebViews).
BusymateAI.registerPageTools([
{
name: "check_order_status",
description: "Look up an order by its number and say where it is.",
inputSchema: {
type: "object",
properties: { orderNumber: { type: "string", description: "The order number, as printed on the receipt" } },
required: ["orderNumber"],
additionalProperties: false,
},
annotations: { readOnlyHint: true },
execute: async ({ orderNumber }) => (await fetch(`/api/orders/${orderNumber}`)).json(),
},
]);
```
`execute` runs **in your page**, on the visitor's own session — the assistant never receives your cookies, tokens or markup, only what you return.
Mark every read with `annotations: { readOnlyHint: true }`. Anything unmarked counts as a change: the assistant shows the exact call and waits for a yes, with no way off.
Give every argument in `inputSchema.properties` a one-line `description` — the assistant reads it to decide what to pass, and one with none is a guess. Each tool validates independently: a real mistake (a bad name, no `execute`) is logged with the exact field and excluded while the rest of the call registers; a missing description registers with a console warning naming it.
With a bundler, import `registerPageTools` from `/sdk/v1/webmcp/index.js` on your assistant's address instead of the global.
## 3. Forms you already have
If the action is a real `
```
The form keeps working as before. Submitting changes something, so it asks first — unless you add `data-tool-readonly` to a form that only filters what is on screen.
## 4. Ask with a form, not a list
When an action needs details the visitor has not given, return a **form card**
rather than asking in prose — and register `sign_in` when something needs their
account, so they never leave the conversation to log in.
[Forms and sign-in inside the chat](https://busymate.ai/docs/guides/form-cards) has both contracts and a
copy-paste example.
## 5. Tools that come and go
A tool the visitor cannot use should not be offered. Pass `{ signal }` from an `AbortController` and `abort()` when the view goes away.
## 6. Who can see them
Tools are exposed only to your assistant's own address by default; any other origin is refused by name. Add one by naming it exactly in `exposedTo` — no wildcards, since `*.example.com` would hand every subdomain the right to run your actions.
**Site page tools**, under Channels and access, is about your VISITORS: turn it off and the Site tools sheet stops appearing, without a site change.
**Assistant may operate the host page**, under Integration › Page tools, is about the ASSISTANT:
| Setting | What the assistant may do |
| --- | --- |
| **No** | Never calls your actions; visitors can still run them from the Site tools sheet. |
| **Read-only actions** | Looks things up — an order status, a cart — but changes nothing; a changing tool is refused before it is offered, and the assistant says plainly it can read but not act. |
| **All actions, with confirmation** | Also runs changes, each confirmed with the visitor in chat first. |
New workspaces start at **All actions, with confirmation** — safe, since nothing is reachable until your page registers a tool naming your assistant in `exposedTo`. The panel takes allow/deny lists by tool-name prefix (blocking wins) and logs every attempt.
## 7. Let a checker see them
The SDK writes `` into your head, rewritten
whenever your tool list changes, so an inspector can confirm registration
without a console. With a server, serve that document at a real URL and link it
from your served head — the only version a reader sees without running the page.
## Verify
1. Open your site and press **Site tools** — every tool is listed with its description, arguments and read-only mark.
2. Run a read-only tool from it; the result matches what your page shows.
3. Run one that changes something: it asks first, showing the exact call; declining leaves the page untouched.
4. Ask the assistant in plain words — it picks your tool rather than describing a page.
5. Open the page in Safari or your mobile app: the list is identical, the bridge covering what the browser does not.
6. Navigate away from the view you registered on: the tool leaves the list.
7. View the page source: a `webmcp-catalog` link is present and lists the same tools.
### Do I need Chrome for this to work?
No. Where the browser implements WebMCP your tools register with it; everywhere else — Safari, Firefox, any iOS or Android WebView — the assistant uses its own bridge. You write them once either way.
### Can the assistant run something without asking?
Only what you marked `readOnlyHint`. Everything else stops at a confirmation showing the exact call and arguments, until the visitor agrees.
### What can the assistant see?
Only what your `execute` returns — never your session, storage or markup, and treated as content rather than instructions, so page text cannot redirect it.
### Can another site use my tools?
No. They are exposed to an exact list of origins — your assistant's address by default — and a call from elsewhere is refused by name, not ignored.
### I already wrote the browser API by hand. Do I have to change?
No. The one call registers on that browser API where it exists and adds the bridge where it does not, so the reason to switch is reach, not correctness.
## Next
- **[Forms and sign-in inside the chat](https://busymate.ai/docs/guides/form-cards)** — cards a tool asks with, and in-chat sign-in.
- **[Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server)** — actions on your servers rather than the page.
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — so a page tool can answer about *this* customer's order.
---
---
title: "Forms and sign-in inside the chat | AI Assistant"
description: "Ask for missing details as a card with real fields instead of a list to type, and let visitors sign in to your site without leaving the conversation."
last_updated: "2026-09-15T19:31:30+03:00"
---
# Forms and sign-in inside the chat | AI Assistant
Source: https://busymate.ai/docs/guides/form-cards
Last modified: 2026-09-15T19:31:30+03:00
AI Assistant can ask for what it is missing as a **form card** — labelled fields and one button, inside the conversation — instead of writing "I'll need: 1. your name 2. your phone number". On a phone that difference is the whole experience: the right keyboard per field, the visitor's own autofill, and nothing to remember. The same card lets a visitor authenticate on *your* site while the conversation keeps going.
Diagram: A tool answers with a field list; the chat draws the card; the submitted values go back to the tool
## 1. Decide which action needs details
Any action the visitor asks for that you cannot complete from what they said: a booking that needs a name and a phone number, an order lookup that needs the order number, a return that needs a reason. Write down the fields, their types, and which are required.
## 2. Answer with a card instead of a result
A tool asks for a form by returning one, in place of its answer:
```javascript
BusymateAI.registerPageTools([
{
name: "book_table",
description: "Book a table. Call with no arguments to show the booking form.",
inputSchema: { type: "object", properties: {} },
annotations: { readOnlyHint: false },
async execute(input) {
// No details yet? Ask for them as a CARD, not as a sentence.
if (!input?.name) {
return {
$bmForm: 1,
title: "Book a table",
description: "Two minutes and you're done.",
fields: [
{ name: "name", label: "Your name", type: "text", required: true, autocomplete: "name" },
{ name: "phone", label: "Phone", type: "tel", required: true, autocomplete: "tel" },
{ name: "party", label: "People", type: "number", min: 1, max: 12 },
{ name: "time", label: "Time", type: "time" },
],
submit: { label: "Book it", tool: "book_table" },
cancel: { label: "Not now" },
};
}
// Submitted: the same tool, now with the visitor's values.
const response = await fetch("/api/bookings", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(input),
});
return response.ok ? await response.json() : { error: "could_not_book" };
},
},
]);
```
Answer with `$bmForm: 1` and a `fields` array. `submit.tool` names where the values go — usually the same tool, now with arguments. A field `type` is `text`, `textarea`, `tel`, `email`, `number`, `date`, `time`, `select` or `password`, with optional `required`, `placeholder`, `help`, `autocomplete`, `min`/`max`, and `options` for a select. Twelve fields at most.
## 3. Or declare it from your MCP server
A [connected MCP server](https://busymate.ai/docs/guides/connect-mcp-server) needs no page: attach the identical object at `_meta.ui.form` on the tool result. That is the same declaration channel as `_meta.ui.resourceUri` — one says "mount my document", the other "draw these fields" — and both render on the **visitor's** surface, the embedded widget and your hosted chat, not only in the Console.
If you write neither, the assistant still shows a card: it asks for the details it is missing with the platform's own form rather than a list. Your own tool is better, because it knows its fields and completes the action as well as collecting it.
That same card appears when an argument was never the visitor's to give. Before any tool runs, the platform checks each string argument against the visitor's own words: a value they typed goes through, a value that looks generated (`customer@example.com`, `John Doe`, `123-456-7890`, `12345`) or a required value that appears nowhere in what they said is **not sent** — the field is asked for instead. So your tool is never handed an invented email to write into a real record, and you do not need a guard of your own for it. Optional arguments the assistant is meant to choose, values from an `enum`, and dates it worked out from "tomorrow" are untouched.
## 4. Sign a visitor in without leaving the chat
If what they asked for needs their account, the honest answer is not "go and find the Sign in button". Sign-in is the same contract under a standard name: register `sign_in` — a [page tool](https://busymate.ai/docs/guides/page-tools) or a tool on your connected server — and return a form card from it. `sign_up` and `sign_out` are its optional companions.
```javascript
BusymateAI.registerPageTools([
{
name: "sign_in",
description:
"Show the sign-in form in the chat when something needs the visitor's account.",
inputSchema: { type: "object", properties: {} },
// Showing a form changes nothing, so no confirmation stands in front of it.
annotations: { readOnlyHint: true },
execute: () => ({
$bmForm: 1,
title: "Sign in",
description: "You'll stay right here in this conversation.",
fields: [
{ name: "email", label: "Email", type: "email", required: true, autocomplete: "username" },
{ name: "password", label: "Password", type: "password", required: true, autocomplete: "current-password" },
],
submit: { label: "Sign in", tool: "sign_in_submit" },
cancel: { label: "Not now" },
}),
},
{
name: "sign_in_submit",
description: "Complete the sign-in the card collected.",
inputSchema: { type: "object", properties: {} },
annotations: { readOnlyHint: false },
async execute({ email, password }) {
const response = await fetch("/api/login", {
method: "POST",
credentials: "same-origin",
headers: { "content-type": "application/json", "x-csrf-token": window.CSRF_TOKEN },
body: JSON.stringify({ email, password }),
});
// `signedIn: true` is the ONE thing the chat reads. On it, the widget
// re-asks your page for an identity token and the SAME conversation
// continues signed in — no reload, nothing retyped.
if (!response.ok) return { signedIn: false, error: "invalid_credentials" };
return { signedIn: true };
},
},
]);
```
Your card is drawn as you wrote it, and it may have as many steps as your login does: a submit that answers with another form replaces the card with the next step. [Let your users sign in from the chat](https://busymate.ai/docs/guides/in-chat-sign-in) walks through the passwordless, two-step version.
A `password` field is accepted only on a card that submits back into your own page, so its value goes from the input straight to your `execute` — never to the assistant, the transcript or a log, and the settled card shows `•••••` rather than the value or its length. Send your CSRF token and rate-limit the endpoint as you already do: this is a form on your page.
Register none of it and there is no sign-in card: the assistant falls back to your real sign-in link rather than standing something of ours in for your login.
## 5. Let the conversation carry on
When your sign-in tool answers `{ signedIn: true }`, the widget asks your page for a fresh identity token — the `getIdentity` handoff in [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors) — and re-mints the session **in place**. Nothing reloads, the visitor retypes nothing, and what they asked for before signing in is answered straight after. The token is verified against your registered JWKS exactly as at launch, so signing in here grants nothing a normal sign-in would not.
## Verify
1. Ask for something your tool needs details for. A card appears with your fields — not a numbered list in a message.
2. On a phone, tap the phone field: the dial pad opens. Tap the email field: the @ row does.
3. Leave a required field empty and submit: it says so and nothing is sent.
4. Submit the form. Your tool runs with the values and the assistant answers from what it returned.
5. Signed out, ask for something needing an account: the sign-in card appears in the chat, not a link away.
6. Sign in from the card. The same conversation continues, now identified, and your original request is answered without repeating it.
7. Open the tool call's details: the password is `•••••`, never the value.
### Does the assistant see what the visitor types?
Only for an ordinary form, where the values become a visible message — which is what a booking or an order number should be. A card that submits into your own page never shows its values to the assistant, and a `password` field is only accepted on that kind of card.
### What if my tool returns something that is not a form?
Nothing changes. The card only renders for a result that positively declares `$bmForm: 1` with at least one usable field; everything else shows as it always has.
### Can I use it without WebMCP page tools?
Yes. A connected MCP server declares the same object at `_meta.ui.form`, and the platform's own card needs nothing from you at all.
### Why not just link to my login page?
A link ends the conversation. The visitor signs in somewhere else, comes back to a chat that may have moved on, and retypes their question. The card keeps the thread.
## Next
- **[Let the assistant use your page](https://busymate.ai/docs/guides/page-tools)** — registering the tools a card belongs to.
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — the identity handoff a sign-in card completes.
- **[Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server)** — declaring a card from a server instead of a page.
---
---
title: "Let your users sign in from the chat | AI Assistant"
description: "Give customers a sign-in card inside the chat, using the accounts you already run: what they get, how to switch it on, and what stays with you."
last_updated: "2026-09-15T19:31:30+03:00"
---
# Let your users sign in from the chat | AI Assistant
Source: https://busymate.ai/docs/guides/in-chat-sign-in
Last modified: 2026-09-15T19:31:30+03:00
AI Assistant lets a customer sign in without leaving the conversation, with the account they already have with you. You implement one tool; your sign-in card opens in the thread the moment a question needs their account, the credentials go to your own site, and the same conversation carries on signed in. No platform account is created for them, and nothing about their login reaches the assistant.
Diagram: A question needs an account; a sign-in card opens in the thread; the same conversation continues signed in, vouched for by a short-lived proof your site signs
## Yours end to end
- **Sign-in and sign-up are your product's.** The card, the login page and the account creation flow are the ones you already run — the form you return is drawn verbatim, not adapted to a template of ours. AI Assistant never stores a password or creates a customer account; it only verifies the short-lived proof your site signs afterwards.
- **What the assistant learns** is an unchanging customer id and, if you allow it, a few display claims such as a name or plan. Everything else stays behind your API and is fetched per request.
## 1. Register your accounts as the identity provider
Open [Console → End-user identity](https://busymate.ai/console/identity), or call `upsert_tenant_identity_provider`. Enter your issuer, JWKS URL, audience, workspace and subject claims, the maximum proof age and the mint endpoint described in [Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors). Add your **customer login URL** as well — the hosted chat and the silent connect in step 4 use it. Save, press **Test identified launch**, then publish.
The check is standards-only: an ES256 (or RS256) JWT verified against your published JWKS for issuer, audience, subject, workspace claim, the nonce the launch asked for, a one-time `jti`, age and expiry. A failed check names the claim, never the value.
```typescript
import { SignJWT, importJWK } from "jose";
// POST https://YOUR-PRODUCT-DOMAIN/api/bmai/identity — requires YOUR OWN logged-in product session.
app.post("/api/bmai/identity", requireSession, async (req, res) => {
// Fresh on EVERY mint. Never persist the launch token/nonce in localStorage,
// sessionStorage, cookies, React state, or module state: every assistant
// launch consumes this pair exactly once.
const nonce = typeof req.body?.nonce === "string" ? req.body.nonce : "";
if (!/^[A-Za-z0-9_-]{32,200}$/.test(nonce)) return res.status(400).json({ error: "invalid_nonce" });
const key = await importJWK(JSON.parse(process.env.AI_LAUNCH_PRIVATE_JWK), "ES256");
const token = await new SignJWT({
tenant_id: process.env.BMAI_TENANT_ID, // the tenant id shown in Console → Identity
nonce, // equals the sibling field below
name: req.user.displayName, // optional low-sensitivity display claim
})
.setProtectedHeader({ alg: "ES256", kid: process.env.AI_LAUNCH_KEY_ID })
.setIssuer(process.env.BMAI_ISSUER) // the Issuer you registered in Console → Identity
.setAudience("busymate-ai")
.setSubject(req.user.id) // IMMUTABLE internal account id; never email/phone/session id
.setJti(crypto.randomUUID()) // one-time (replay-protected)
.setIssuedAt()
.setExpirationTime("120s") // <= registered max age (120s)
.sign(key);
res.set("Cache-Control", "no-store");
res.status(201).json({ token, nonce, expiresIn: 120 });
});
```
## 2. Put a sign-in card in the chat
Sign-in is a **standard widget tool you implement**. Register a tool named `sign_in` — as a page tool on any page where the chat is embedded, or on your [connected MCP server](https://busymate.ai/docs/guides/connect-mcp-server) — and return a form card from it:
```javascript
BusymateAI.registerPageTools([
{
name: "sign_in",
description:
"Show the sign-in form in the chat when something needs the visitor's account.",
inputSchema: { type: "object", properties: {} },
// Showing a form changes nothing, so no confirmation stands in front of it.
annotations: { readOnlyHint: true },
execute: () => ({
$bmForm: 1,
title: "Sign in",
description: "You'll stay right here in this conversation.",
fields: [
{ name: "email", label: "Email", type: "email", required: true, autocomplete: "username" },
{ name: "password", label: "Password", type: "password", required: true, autocomplete: "current-password" },
],
submit: { label: "Sign in", tool: "sign_in_submit" },
cancel: { label: "Not now" },
}),
},
{
name: "sign_in_submit",
description: "Complete the sign-in the card collected.",
inputSchema: { type: "object", properties: {} },
annotations: { readOnlyHint: false },
async execute({ email, password }) {
const response = await fetch("/api/login", {
method: "POST",
credentials: "same-origin",
headers: { "content-type": "application/json", "x-csrf-token": window.CSRF_TOKEN },
body: JSON.stringify({ email, password }),
});
// `signedIn: true` is the ONE thing the chat reads. On it, the widget
// re-asks your page for an identity token and the SAME conversation
// continues signed in — no reload, nothing retyped.
if (!response.ok) return { signedIn: false, error: "invalid_credentials" };
return { signedIn: true };
},
},
]);
```
Whatever you return is rendered as you wrote it: your fields, your labels, your order, your wording, your links. Nothing of ours is added to your form, and there is no platform styling of your login to design around. Two companion names complete the set and both are optional — `sign_up` to create an account, `sign_out` to end the session — on the identical contract.
The fields are yours: email and password, an emailed code, or a single button that starts the sign-in you already offer. A `password` field is accepted only on a card that submits back into your own page, so the value goes from the input to your `execute` and nowhere else, and the settled card shows `•••••`. When your tool answers `{ signedIn: true }`, the widget asks your page for a fresh proof through `getIdentity` and re-mints the session in place — same thread, now identified, the earlier question answered. The full field vocabulary and its limits are in [Forms and sign-in inside the chat](https://busymate.ai/docs/guides/form-cards).
### As many steps as your login has
A form may answer with a form: whatever your submit tool returns is drawn next, in the same card. A choice of *Email* or *Phone*, then your fields, a display-name field, a consent checkbox, links to your own terms and privacy, then a one-time code — each step posting to your own endpoint — is still the one `sign_in` tool and nothing new to wire.
Hold anything a step needs to remember in your own page, the way the snippet below keeps the contact it just sent a code to. The form has no hidden field, and one invented would simply be dropped.
### No passwords? Use a one-time code
If you sign customers in with a code to a phone or an email, the card is two steps: ask where to send the code, then ask for the code.
```javascript
// The contact the code was sent to. It lives HERE, in your page, for the
// one hop between the two cards: the form spec has no hidden-value field, and
// inventing one would simply be dropped by the parser.
let pendingContact = null;
BusymateAI.registerPageTools([
{
name: "sign_in",
description:
"Show the sign-in form in the chat when something needs the visitor's account.",
inputSchema: { type: "object", properties: {} },
// Showing a form changes nothing, so no confirmation stands in front of it.
annotations: { readOnlyHint: true },
execute: () => ({
$bmForm: 1,
title: "Sign in",
description: "We'll text or email you a one-time code. You'll stay right here.",
fields: [
// `autocomplete: "username"` is not decoration. The chat reads it to
// know this box asks for a CONTACT, and refuses a bare one-time code
// typed here before your endpoint is ever called (#3518) — the
// predictable mistake, since the next card asks for exactly that code.
{ name: "contact", label: "Phone or email", type: "text", required: true, autocomplete: "username" },
],
submit: { label: "Send me a code", tool: "sign_in_send_code" },
cancel: { label: "Not now" },
}),
},
{
name: "sign_in_send_code",
description: "Send the one-time code, then ask for it.",
inputSchema: { type: "object", properties: {} },
annotations: { readOnlyHint: false },
async execute({ contact }) {
// Belt and braces: the card refuses a bare code, and so does this. A
// tool that answers "sent" for a value nothing can be sent to leaves the
// visitor waiting for a message that will never arrive.
if (/^\d{4,8}$/.test(String(contact ?? "").replace(/\s+/g, ""))) {
return { signedIn: false, error: "not_a_contact" };
}
const response = await fetch("/api/auth/otp/start", {
method: "POST",
credentials: "same-origin",
headers: { "content-type": "application/json", "x-csrf-token": window.CSRF_TOKEN },
body: JSON.stringify({ contact }),
});
if (!response.ok) return { signedIn: false, error: "could_not_send_code" };
pendingContact = contact;
// A form may answer with a form. This is the SECOND card, in the same
// conversation — the visitor never leaves and never retypes anything.
return {
$bmForm: 1,
title: "Enter your code",
description: "We sent a 6-digit code. It expires shortly.",
fields: [
// NOT `password`: a one-time code is not a stored credential, and
// `one-time-code` is what lets a phone offer the SMS it just got.
{ name: "code", label: "Code", type: "text", required: true, autocomplete: "one-time-code" },
],
submit: { label: "Sign in", tool: "sign_in_submit" },
cancel: { label: "Not now" },
};
},
},
{
name: "sign_in_submit",
description: "Complete the sign-in the card collected.",
inputSchema: { type: "object", properties: {} },
annotations: { readOnlyHint: false },
async execute({ code }) {
const response = await fetch("/api/auth/otp/verify", {
method: "POST",
credentials: "same-origin",
headers: { "content-type": "application/json", "x-csrf-token": window.CSRF_TOKEN },
body: JSON.stringify({ contact: pendingContact, code }),
});
// `signedIn: true` is the ONE thing the chat reads. On it, the widget
// re-asks your page for an identity token and the SAME conversation
// continues signed in — no reload, nothing retyped.
if (!response.ok) return { signedIn: false, error: "invalid_code" };
return { signedIn: true };
},
},
]);
```
Give the code field `autocomplete: "one-time-code"` and leave its type as `text`: a one-time code is not a stored credential, and `text` is what lets a phone offer the SMS it just received. Do not answer a sign-in request with a tool that returns an explanation of how to log in — the chat will say a card is coming and then read your customer a paragraph instead.
### If you implement nothing
Both snippets above are starting points, not the contract — a tool of your own that returns the same form is exactly as good, and a login neither of them fits is a reason to write your own, not a reason to skip it.
If you register no tool at all, nothing is invented on your behalf. There is no card, and the assistant will not promise one: it answers with your real sign-in link, or sends the visitor to your login URL at the top level (step 3). Publish neither and it offers nothing rather than an errand with nowhere to go.
## 3. Know what happens where
| Where the chat runs | What the Sign in control does |
|---|---|
| Embedded on your page, `sign_in` registered | The card opens in the thread. Nothing leaves the page. |
| Embedded on your page, no `sign_in` tool | Your page navigates to your login URL with `return_to` and a one-time `bmai_nonce`, then returns and hands the proof to the widget. |
| Your hosted chat address | The same redirect: to your login URL and back to the exact `return_to`, proof in the URL fragment. |
| Your iOS or Android app | The app receives the request over the native bridge and runs its own login. |
| No `sign_in` tool and no login URL published | Nothing is offered. The assistant does not announce a card it cannot show. |
A credential is never typed into a frame that is not yours. The card submits into your page, and a redirect always happens at the top level, on your origin.
## 4. Connect a customer who is already signed in
If the customer already has a session with your product, the chat can inherit it without showing a form. On the hosted address and in the embed, the widget opens your login URL once per tab in a hidden frame with `bmai_prompt=none` — the `prompt=none` meaning every identity stack knows: answer only from an existing session, never render a form. Your login redirects straight back with the proof in the fragment, and the session is re-minted signed in. It works when:
- your customer login URL is published on the current revision;
- your login honours `bmai_prompt=none` — an existing session answers, a signed-out visitor is sent back with nothing;
- the probe finishes within three seconds; it runs once per tab and fails silently, so the visitor simply stays a guest with the Sign in control still there.
This can only add an identity. It never blocks the chat, never redirects the visible page, never traps anyone in a login form, and is not attempted inside a native app, which passes identity over its own bridge.
## 5. Choose what a signed-in customer can do
Each connected tool carries an access level. **Public** tools answer anyone. **Identified** tools run only after the proof was verified and receive your customer id. **Delegated** tools also need *Allow delegated customer account access* on the provider and act through a signed actor token or a per-customer consent. Set the levels in [Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server). The sites at [demo.busymate.ai](https://demo.busymate.ai) each include a demo customer to sign in as: the same card, then orders, bookings or balances for that one person.
## Verify
1. Signed out, ask something that needs an account: the card appears in the thread, not a link away.
2. Sign in from the card: the same conversation continues and your earlier question is answered.
3. Open the tool call's details: the password shows as `•••••`.
4. Sign in to your product in another tab, then open the hosted chat: it is signed in with no form shown.
5. A second customer cannot see the first one's history or account.
### Do my customers need an account with AI Assistant?
No. They sign in to your product. Your site signs a short-lived proof, the assistant verifies it against your public key, and the subject is your own customer id.
### Where do sign-ups happen?
In your product, the way they do today. The card or the login page you return is yours, so a new customer creates an account with you and comes back to the chat signed in.
### Can the assistant read the password?
No. A password field is accepted only on a card that submits into your own page. The value never enters a tool argument, the transcript or a log.
### What if the silent connect is not answered?
Nothing visible happens. The probe expires after three seconds, the visitor stays a guest and the Sign in control is still there.
### What happens if I never add the `sign_in` tool?
There is no card, and the assistant says so plainly instead of promising one: it gives your real sign-in link, or sends the visitor to your login URL at the top level. Nothing of ours stands in for your login.
## Next
- **[Recognize signed-in customers](https://busymate.ai/docs/guides/identified-visitors)** — the keys, the claims and the mint endpoint.
- **[Forms and sign-in inside the chat](https://busymate.ai/docs/guides/form-cards)** — the card contract in detail.
- **[Connect your MCP server as assistant tools](https://busymate.ai/docs/guides/connect-mcp-server)** — the identified tools it unlocks.
---
---
title: "How your page and the widget talk to each other | AI Assistant"
description: "The complete host ↔ widget contract: open, preset, ask and identify from the page; the events the widget posts back; page tools both ways; navigation, theming, security and a cookbook."
last_updated: "2026-09-23T23:00:24+03:00"
---
# How your page and the widget talk to each other | AI Assistant
Source: https://busymate.ai/docs/guides/widget-page-api
Last modified: 2026-09-23T23:00:24+03:00
The widget your visitors see is one script tag; the page that carries it can drive it, listen to it, and hand it tools. This is the complete contract between the two — every call in one direction, every message in the other — with a runnable copy of each on the [playground](https://busymate.ai/playground).
Diagram: Your page calls the widget through window.BusymateAI; the widget posts busymate.ai.v1 messages back; page tools flow both ways through the standard model-context surface
Two parts do the work. The **launcher** is the script itself: it draws the button and the panel, owns their geometry, and installs `window.BusymateAI`. The **frame** is the chat, loaded from the assistant's origin inside that panel. Your page only ever talks to the launcher; the launcher talks to the frame over a pinned `postMessage` channel and never trusts a message from anywhere else.
## Mount
One tag, before ``. `data-assistant` is your workspace slug (the Console's Integration page prints the real one); the three optional attributes are the launcher's visible label, the frame's title and the button's accessible name.
```html
```
The tag is `async`: nothing on your page waits for it. Every call below is safe to make before it has loaded — commands queue and deliver once the frame is up.
### Declare your launcher's look
A look you set in the Console (Brand › Launcher button) reaches your page as a small script of its own, a moment after the launcher. The launcher cannot know it before it first paints, so by default it draws the standard button and restyles it when the look arrives. One attribute on the tag removes that moment, and the Console shows the one your settings need:
- `data-launcher="custom"` holds the button, laid out but invisible, until your look has loaded. It never waits longer than 0.8 seconds; if the look cannot load, the standard button appears.
- `data-launcher="hidden"` never shows the button. Use it when your page opens the chat from its own control (`BusymateAI.open()`). It holds even when your CSP or a content blocker stops the look from loading.
Leave it off and nothing waits: the standard button appears at once, as it always has.
## Open and preset
Four calls drive the panel; two more preset what the visitor sees. Nothing is persisted by the frame: your page owns the panel's theme and language only while it embeds it.
```javascript
// The loader installs window.BusymateAI. Every call is safe before the frame
// has loaded — commands queue and deliver on load.
BusymateAI.open();
BusymateAI.close();
BusymateAI.toggle();
BusymateAI.isOpen(); // → true | false
```
```javascript
// Preset the panel from the page: colour scheme + language.
// Neither is persisted by the frame — your page owns them while it embeds it.
BusymateAI.setTheme("dark"); // "light" | "dark" | "system"
BusymateAI.setLocale("de"); // any BCP 47 tag the platform serves
BusymateAI.setLocale(null); // unpin — the frame follows the visitor again
BusymateAI.open();
```
| Call | Does | Returns |
| --- | --- | --- |
| `open()` / `close()` / `toggle()` | show, hide, flip the panel | — |
| `isOpen()` | the current state | `boolean` |
| `setTheme(theme)` | `"light"`, `"dark"` or `"system"` (hands the choice back to the visitor's browser) | `false` on any other value |
| `setLocale(tag)` | a BCP 47 tag the platform serves; `setLocale(null)` unpins it — the frame follows the visitor again | `false` on a malformed tag |
### Make room for the panel
By default the panel floats **over** your page and your page never moves. On a layout that runs edge to edge — a full-bleed hero, a headline or a column reaching the right margin — the open panel then sits on top of it. `data-layout="push"` on the script tag opts you into the other behaviour: while the panel is open, the launcher reserves exactly the column it occupies as `padding-right` on ``, and hands it straight back on close.
```html
```
Five things worth knowing before you turn it on:
- **Desktop only.** Below 1100px nothing is reserved, and a window dragged narrower than that mid-session gives the column back immediately. A phone, where the panel is full-screen anyway, is never pushed.
- **It is the panel's real width**, measured — including a size the visitor dragged the panel to — not a fixed 420. The same number is published as `--bmai-chat-reserve` on `` and as `detail.reserved` on the open event.
- **Full-bleed blocks need one rule of your own.** A section painted with `width: 100vw; margin-left: calc(50% - 50vw)` is anchored to the *viewport*, which no amount of root padding narrows — so it keeps its old width and slides off the left edge. `--bmai-chat-page-width`, published beside the reserve, is the exact width such a block should take (the root's client width minus the reserve, scrollbar already excluded); the snippet above is the whole fix. The WordPress plugin ships this rule for `.alignfull` / `.alignwide` already, so a WordPress site needs nothing.
- **The launcher owns `padding-right` and `overflow-x` on `` in this mode.** If your theme styles the root element's padding, move that to a wrapper. `overflow-x: clip` is there so nothing you have not thought of can add a horizontal scrollbar.
- **It animates**, unless the visitor has asked for reduced motion, in which case it snaps.
A launcher you dock bottom-left in the Console reserves its column on the other side: `padding-left` on ``, with `data-bmai-chat-reserve="left"` so your own rules can tell.
Leave the attribute off (or set anything other than `push`) and the widget behaves exactly as it always has.
## Page to chat
`ask(text)` opens the panel and delivers the prompt to the frame's own composer path — the same append a typed message takes, never a synthetic key press — so every guard the frame applies to a typed message applies here too. Add `{ submit: false }` to fill the composer and let the visitor press send.
```javascript
// Open the panel and send a prompt — the same append path a typed message takes.
BusymateAI.ask("What can you do on this page?");
```
```javascript
// Fill the composer only; the visitor reads, edits and presses send.
BusymateAI.ask("Book a table for two on Friday at 19:00", { submit: false });
```
```html
```
`ask` returns `false` for empty text and `true` otherwise. Calls made before the frame loads queue, eight at most, oldest dropped first. A curated "try this" prompt is the same call — the playground's example prompts are nothing more:
```javascript
// Every example prompt on this page is one call — the same call your
// own "try it" buttons make.
BusymateAI.ask("Compare the plans and recommend one for a two-person shop.");
```
## Chat to page
The frame posts messages to your page. The launcher already acts on every one of them — collapses the panel, opens a new tab, navigates in place — so your page listens only to observe. Check `event.origin` against the assistant's origin, then read `type`.
> **On a custom domain.** Hardcoding `"https://busymate.ai"` breaks the moment your workspace serves the assistant from its own domain — the frame's real origin is wherever `embed/v1.js` was loaded from, not always the platform apex. Derive it once instead of hand-typing it at every listener:
> ```javascript
> const ORIGIN = window.BusymateAI?.origin
> ?? new URL(document.currentScript?.src ?? document.querySelector('script[data-assistant]').src).origin;
> ```
> then compare against `ORIGIN`, not a literal string, everywhere below.
```javascript
// The frame posts busymate.ai.v1.* messages to your page. The loader already
// acts on every one of them; your page only listens to OBSERVE.
// audit docs-and-developer-path-08 (2026-09-18): derived, never a hardcoded
// literal — a workspace on its own custom domain serves the frame from a
// different origin than the one this doc happens to render on.
const ORIGIN = window.BusymateAI?.origin
?? new URL(document.currentScript?.src ?? document.querySelector('script[data-assistant]').src).origin;
window.addEventListener("message", (event) => {
if (event.origin !== ORIGIN) return;
const { type, ...data } = event.data ?? {};
if (typeof type !== "string" || !type.startsWith("busymate.ai.v1.")) return;
console.log(type, data);
// busymate.ai.v1.ready { visitorKind, displayClaims } a session is live
// busymate.ai.v1.close — ✕ pressed inside the frame
// busymate.ai.v1.navigate { href } a same-origin link was clicked
// busymate.ai.v1.open_url { url } any other link (new tab)
// busymate.ai.v1.resize_to { w, h } resize_end resize_by { dw, dh }
});
```
| `type` | Payload | When |
| --- | --- | --- |
| `busymate.ai.v1.ready` | `visitorKind`, `displayClaims` | a session is live — anonymous or recognised |
| `busymate.ai.v1.close` | — | the ✕ inside the frame was pressed |
| `busymate.ai.v1.navigate` | `href` | a link on your own origin was clicked in a reply |
| `busymate.ai.v1.open_url` | `url` | any other link (the launcher opens a new tab) |
| `busymate.ai.v1.resize_to` / `resize_end` / `resize_by` | `w, h` / — / `dw, dh` | the visitor is resizing the panel by its corner |
| `busymate.ai.v1.identity_request` / `auth_request` | — / `url` | the identity handshake (Identity, below) |
### Same-site navigation
A link in a reply that points at your own origin navigates your page in place — same tab, the conversation intact. The launcher pushes the new URL, dispatches `popstate` for any router built on the History API, and dispatches a dedicated `busymate:hostnavigate` event for a page that has no router at all. A page that changes nothing within a short window is treated as static and reloaded normally; the panel's open state and the conversation both survive that.
```javascript
// A same-origin link in the chat navigates YOUR page in place (same tab).
// SPA routers already listening for popstate resync on their own; this
// dedicated event needs no router at all.
window.addEventListener("busymate:hostnavigate", (event) => {
const { href } = event.detail;
myRouter.push(new URL(href).pathname);
});
```
### Open state
The panel tells your page when it opens and closes — two ways, both always on, neither needing a message listener or an observer.
For CSS: `` carries `data-bmai-chat="open"` or `data-bmai-chat="closed"`, stamped `closed` from the first frame, so `html[data-bmai-chat="open"] .my-banner { … }` is meaningful before the visitor has opened anything. For scripts: a `bmai:chat-open` / `bmai:chat-close` event on `window`, whose `detail` carries the `assistant` slug (a page may embed two) and `reserved`, the pixels the launcher reserved beside your content — `0` unless you opted into `push` below. Each fires only on a real change, so there is nothing to de-duplicate.
Do not watch for the panel's `