Widget JavaScript API
The AgentMySite widget is a small script, called the loader, that you add to your pages. The loader draws the chat launcher and opens the chat in a sandboxed iframe. It also adds one global object, window.AgentMySite, that your page uses to register AgentActions.
This page covers:
- The embed snippet and the attributes it accepts.
- What the loader does when the page loads.
- The
window.AgentMySiteAPI. - How to install the widget in single-page apps (React and Next.js).
Add the widget to a page
Section titled “Add the widget to a page”Copy the snippet for your site from the admin panel. It looks like this:
<script async src="https://widget.agentmysite.com/loader/widget.js" data-site-key="YOUR_SITE_KEY" data-api-base="https://api.agentmysite.com" data-iframe-origin="https://widget.agentmysite.com"></script>Paste it once per page, just before </body>. The script is async, so it doesn’t block your page from rendering.
Script attributes
Section titled “Script attributes”| Name | Type | Default | Description |
|---|---|---|---|
data-site-key Required | string | — | Your site key from the admin panel. The key is public; it identifies your site and is safe to put in page source. If it is missing, the widget shows nothing. |
data-api-base | URL | https://api.agentmysite.com | The API host for chat sessions and messages. |
data-iframe-origin | URL | value of `data-api-base` | The host that serves the chat iframe. Always set it to https://widget.agentmysite.com. If you leave it out, the iframe tries to load from the API host and the chat does not open. |
data-customer-id | string | — | Optional. Your ID for the signed-in visitor. Shown with the conversation in the admin panel. Up to 128 characters. |
data-customer-name | string | — | Optional. The visitor’s name, shown in the admin panel. Up to 256 characters. |
data-customer-email | string | — | Optional. The visitor’s email, shown in the admin panel. Up to 320 characters. |
data-shop-domain | string | — | Optional. A store domain such as my-store.myshopify.com, for apps where one AgentMySite site serves many stores. Conversation history is kept separately for each store. Up to 253 characters. |
data-single-conversation | "true" | off | Optional. When "true", the widget shows only the visitor’s current conversation and hides the list of past conversations. |
The data-customer-* and data-shop-domain values come from the visitor’s browser and AgentMySite doesn’t verify them. They label conversations for you in the admin panel. Don’t rely on them to decide what a visitor is allowed to see or do.
If a page has more than one script[data-site-key] tag, the loader reads its settings from the last one. Add the snippet only once per page.
What happens when the page loads
Section titled “What happens when the page loads”- The loader adds
window.AgentMySite(seeregisterToolHandler). - It reads the script attributes. If
data-site-keyis missing, it stops. - It asks the AgentMySite API for a short-lived chat session for this visitor. The API checks that the page’s domain is on your allowed list. If the check fails, or the API can’t be reached, the loader stops and shows nothing. It never shows an error on your page.
- It draws the chat launcher, which uses your brand color and assistant name from the admin panel.
- When the visitor clicks the launcher, the loader creates the chat iframe. If you turned on auto-open in the admin panel, the chat opens by itself. This doesn’t happen again in a browser tab where the visitor has closed the chat.
Your appearance and behavior settings (brand color, assistant name, light or dark mode, auto-open) and your assistant’s welcome message and suggestion chips come from the admin panel each time the page loads. They don’t go in the snippet.
Data the loader stores in the browser
Section titled “Data the loader stores in the browser”The loader keeps a few values in your site’s storage, so a visitor who comes back resumes the same conversation:
| Name | Type | Description |
|---|---|---|
agentmysite.visitorId | localStorage | A random visitor ID created on the first visit. |
agentmysite.conversationId.<siteKey> | localStorage | The visitor’s current conversation, so a reload or navigation resumes it. |
agentmysite.mode | localStorage | Whether the visitor docked the chat to the side of the screen. Docking is offered only on screens 768 px or wider. |
agentmysite.teaserDismissed | localStorage | Set when the visitor dismisses the greeting bubble. |
agentmysite.autoOpenDismissed.<siteKey> | sessionStorage | Set when the visitor closes the chat, so auto-open doesn’t reopen it in the same tab. |
If storage is blocked, the widget still works, but it can’t resume conversations across page loads. The chat session token is never written to storage or put in a URL.
window.AgentMySite.registerToolHandler()
Section titled “window.AgentMySite.registerToolHandler()”Registers the function that runs your AgentActions. The assistant calls your tools by name, and the widget passes each call to this function on your page.
Signature
Section titled “Signature”window.AgentMySite.registerToolHandler( handler: (name: string, args: unknown) => unknown | Promise<unknown>): voidParameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
handler Required | function | Called once for each tool call. Receives the tool name and the args object. AgentMySite has already checked args against the tool’s input schema. Return (or resolve to) the result for the assistant. |
The handler receives:
| Name | Type | Description |
|---|---|---|
name | string | The tool name, as you defined it in the admin panel. |
args | object | The tool arguments. They always match the tool’s input schema. |
Return value
Section titled “Return value”registerToolHandler returns undefined.
Your handler should return plain JSON data (an object, array, string, number, boolean, or null), or a promise that resolves to it. The widget sends that value to the assistant as the tool result. Don’t return values that can’t be copied between windows, such as functions, DOM nodes, or class instances with methods.
Behavior
Section titled “Behavior”- One handler is active at a time. If you call
registerToolHandleragain, the new handler replaces the old one. Use a single handler that switches onname. - Passing something that isn’t a function removes the current handler.
- The handler runs in your page, with your page’s cookies and session. The widget iframe never sees them.
window.AgentMySiteexists as soon as the widget script runs, before the chat session starts. You can register your handler before the visitor opens the chat.- If
window.AgentMySitealready exists when the loader runs, the loader addsregisterToolHandlerto it and keeps your other properties.
Example
Section titled “Example”The widget script is async, so window.AgentMySite might not exist yet when your own script runs. This helper waits for it. Put it after the widget script tag:
<script> function onAgentMySiteReady(cb) { if (window.AgentMySite && typeof window.AgentMySite.registerToolHandler === 'function') { cb(window.AgentMySite); return; } var tag = document.querySelector('script[data-site-key]'); tag.addEventListener('load', function () { cb(window.AgentMySite); }, { once: true }); }
onAgentMySiteReady(function (agentMySite) { agentMySite.registerToolHandler(async function (name, args) { if (name === 'get_order_status') { const res = await fetch('/api/orders/' + encodeURIComponent(args.orderNumber) + '/status'); if (!res.ok) throw new Error('Order lookup failed'); return await res.json(); } throw new Error('Unknown tool: ' + name); }); });</script>For a complete example, including a tool that asks the visitor to confirm first, see AgentActions.
Errors
Section titled “Errors”When a tool call fails, the loader catches the failure and sends the assistant an error result, so the assistant can explain the problem to the visitor:
| Name | Description |
|---|---|
Your handler throws an Error | The error’s message is sent to the assistant. The visitor might see it, so keep it free of secrets and internal details. |
Your handler throws something other than an Error | The assistant gets Tool handler failed. |
Your handler takes longer than 15 seconds | The assistant gets Tool "<name>" timed out. Any value your handler returns later is ignored. |
No handler is registered | The assistant gets No tool handler registered on the host page. |
Install in a single-page app
Section titled “Install in a single-page app”The loader runs once, when its script loads. It doesn’t watch for route changes, and it doesn’t need to: the launcher and the chat stay on the page while your app changes routes. Because the conversation ID is stored in the browser, a full reload resumes the same conversation too.
Follow these rules in a single-page app:
- Load the script once, from a component that stays mounted for the whole app (your root layout), not from each page.
- Don’t add the script twice. A second copy draws a second launcher.
- The widget reads the page address (origin and path only, without query string or fragment) when the chat first opens. If the visitor then moves to another route without a reload, the widget still has the first address.
import { useEffect } from 'react';
export function AgentMySiteWidget() { useEffect(() => { if (document.querySelector('script[data-agentmysite-loader]')) return; const s = document.createElement('script'); s.src = 'https://widget.agentmysite.com/loader/widget.js'; s.async = true; s.setAttribute('data-agentmysite-loader', ''); s.setAttribute('data-site-key', 'YOUR_SITE_KEY'); s.setAttribute('data-api-base', 'https://api.agentmysite.com'); s.setAttribute('data-iframe-origin', 'https://widget.agentmysite.com'); s.addEventListener('load', () => { window.AgentMySite.registerToolHandler(async (name, args) => { // Handle your tools here. See the AgentActions page. throw new Error('Unknown tool: ' + name); }); }); document.body.appendChild(s); }, []); return null;}Render <AgentMySiteWidget /> once, in your top-level App component.
'use client';
import Script from 'next/script';
export function AgentMySiteWidget() { return ( <Script src="https://widget.agentmysite.com/loader/widget.js" strategy="afterInteractive" data-site-key="YOUR_SITE_KEY" data-api-base="https://api.agentmysite.com" data-iframe-origin="https://widget.agentmysite.com" onLoad={() => { (window as any).AgentMySite.registerToolHandler(async (name: string, args: unknown) => { // Handle your tools here. See the AgentActions page. throw new Error('Unknown tool: ' + name); }); }} /> );}import { AgentMySiteWidget } from './agentmysite-widget';
export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body> {children} <AgentMySiteWidget /> </body> </html> );}The root layout stays mounted across client-side navigation, so the script loads only once.
If you need to remove the widget, for example when a user signs out of an embedded app, remove the script tag and the elements with data-testid="agentmysite-bubble" and data-testid="agentmysite-widget-iframe". The loader doesn’t offer a destroy() method.
What the API doesn’t include yet
Section titled “What the API doesn’t include yet”Right now, registerToolHandler is the only method on window.AgentMySite. There are no methods to open or close the chat from your code, to identify a visitor after the page loads, or to listen for widget events. To label signed-in visitors, use the data-customer-* attributes. For a server-side API, see REST API.
Related
Section titled “Related”- AgentActions: let the assistant act on your site.
- Security model: tokens, allowed domains, rate limits, and the Content Security Policy hosts to allow.
Was this page helpful?
Thanks for the feedback.