AgentActions
Normally the assistant answers from your content. With AgentActions, it can also take actions on your site, such as looking up an order or cancelling a booking.
An AgentAction is a tool. You describe the tool in the admin panel, and you write the code that runs it on your page. When a visitor asks for something a tool can do, the assistant calls the tool, your code runs in the visitor’s browser with your site’s own session, and the assistant answers from the result.
AgentMySite never runs your tools on its own servers, and it never sees your site’s cookies or credentials.
How a tool call works
Section titled “How a tool call works”-
You define the tool in the admin panel: a name, a description, an input schema, and whether it changes anything.
-
A visitor asks a question. The assistant sees the tools you’ve enabled and decides to call one, with arguments.
-
AgentMySite checks the arguments against your input schema. If they don’t match, the call stops here: your page is never asked to run it, and the assistant gets the error back so it can correct itself or explain.
-
If the tool needs confirmation, the chat shows the visitor a card with the tool name and every argument, plus Confirm and Cancel. Nothing runs until the visitor confirms.
-
The widget passes the call to the handler you registered with
window.AgentMySite.registerToolHandler(). -
Your handler does the work (usually by calling your own backend) and returns a result.
-
The assistant reads the result and replies to the visitor. It might call another tool.
Define a tool
Section titled “Define a tool”In the admin panel, open Tools and select Add tool. Each tool has these fields:
| Name | Type | Default | Description |
|---|---|---|---|
Name Required | string | — | What your handler switches on, for example get_order_status. Letters, digits, _ and - only, up to 64 characters. You can’t change it after you save. create_support_ticket and names that start with agentmysite_ are reserved. |
Description Required | string | — | What the tool does and when to use it. The assistant reads this to decide when to call the tool, so be specific. |
Input schema Required | JSON Schema | — | The arguments the tool takes, as a JSON Schema object. The top level must set "additionalProperties": false. The assistant also reads the property descriptions. |
Mutating | boolean | off | Turn this on for tools that change something, such as creating, sending, or cancelling. Mutating tools ask the visitor to confirm before they run. |
Require confirmation | Default | Always | Never | Default | **Default** asks for confirmation only when the tool is mutating. **Always** and **Never** override that. |
Enabled | boolean | on | The assistant can call only enabled tools. Turn a tool off to hide it from the assistant without deleting it. |
You can also select Import from API spec to create tools from an OpenAPI (Swagger) document. Imported tools start disabled, so you can review them before the assistant can use them.
At the bottom of the Tools page, the integration panel shows your embed snippet and a starter handler. The starter handler has one case for each enabled tool, ready for you to fill in.
Worked example: order status and cancellation
Section titled “Worked example: order status and cancellation”This example adds two tools to a store:
get_order_statusis read-only, so it runs straight away.cancel_orderis mutating, so the visitor confirms it first.
1. Define the tools
Section titled “1. Define the tools”Add get_order_status:
- Description:
Looks up the status and tracking link of one of the visitor's orders. Use it when the visitor asks where their order is. - Mutating: off
- Input schema:
{ "type": "object", "properties": { "orderNumber": { "type": "string", "pattern": "^[0-9]{3,10}$", "description": "The order number, digits only, for example 1042." } }, "required": ["orderNumber"], "additionalProperties": false}Add cancel_order:
- Description:
Cancels one of the visitor's orders that hasn't shipped yet. Only use it when the visitor clearly asks to cancel. - Mutating: on
- Input schema: the same as
get_order_status.
2. Register the handler
Section titled “2. Register the handler”Add the widget snippet and, after it, your handler. The handler forwards each call to your own backend. credentials: 'include' sends your site’s session cookie, so your backend knows which customer is asking.
<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>
<!-- Place this after the widget script tag. --><script> // Runs cb once window.AgentMySite exists (the widget script is async). 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) { switch (name) { case 'get_order_status': { // Your own backend; the browser sends your site's session cookie. const res = await fetch( '/api/orders/' + encodeURIComponent(args.orderNumber) + '/status', { credentials: 'include' } ); if (res.status === 404) return { found: false }; if (!res.ok) throw new Error('Order lookup failed (' + res.status + ')'); const order = await res.json(); return { found: true, status: order.status, trackingUrl: order.trackingUrl }; } case 'cancel_order': { // Mutating: the visitor already confirmed this call in the chat. const res = await fetch( '/api/orders/' + encodeURIComponent(args.orderNumber) + '/cancel', { method: 'POST', credentials: 'include' } ); if (!res.ok) throw new Error('Could not cancel the order (' + res.status + ')'); return { cancelled: true }; } default: throw new Error('Unknown tool: ' + name); } }); });</script>For React and Next.js, see Install in a single-page app.
3. Do the work on your server
Section titled “3. Do the work on your server”Your backend is the security boundary. Treat every request from the handler like any other request from the browser:
- Check that the user is signed in, and that the order belongs to them.
- Check the arguments again. AgentMySite’s schema check is a first line of defense, not a replacement for yours.
app.get('/api/orders/:orderNumber/status', requireSignedIn, async (req, res) => { const order = await db.orders.findOne({ number: req.params.orderNumber, customerId: req.user.id, // only the signed-in customer's own orders }); if (!order) return res.sendStatus(404); res.json({ status: order.status, trackingUrl: order.trackingUrl });});
app.post('/api/orders/:orderNumber/cancel', requireSignedIn, async (req, res) => { const order = await db.orders.findOne({ number: req.params.orderNumber, customerId: req.user.id }); if (!order) return res.sendStatus(404); if (order.status !== 'unfulfilled') return res.status(409).json({ error: 'already shipped' }); await db.orders.cancel(order.id); // safe to repeat: cancelling twice has no extra effect res.json({ cancelled: true });});4. Try it
Section titled “4. Try it”- Ask the assistant “Where is order 1042?” The handler calls
/api/orders/1042/status, and the assistant answers with the status. You can watch the request in your browser’s developer tools. - Ask “Please cancel order 1042.” The chat shows a confirmation card for
cancel_orderwithorderNumber: 1042. Select Confirm to run your handler, or Cancel to stop.
The confirmation flow
Section titled “The confirmation flow”A tool asks for confirmation when Require confirmation is Always, or when it’s Default and the tool is mutating.
- The card shows the tool name and each argument.
- Confirm runs your handler, and the assistant continues with the result.
- Cancel doesn’t call your handler. The assistant gets an error result with the value
"cancelled"and replies to the visitor, for example to say nothing was changed.
You don’t build any confirmation interface yourself. Keep the Mutating flag accurate, because it’s what protects visitors from actions they didn’t intend.
What the assistant sees
Section titled “What the assistant sees”- Your tools: the name, description, and input schema of each enabled tool.
- Arguments: the assistant writes the arguments. If they don’t match your schema (wrong type, a missing required field, an extra property), your handler is never called. The assistant gets the message
Invalid parameters for <name>: <reason>and can try again or explain. - Results: whatever your handler returns, converted to JSON text. If a link appears in the result (for example a
trackingUrl), the assistant can show it to the visitor as a clickable link. - Errors: if your handler throws, times out, or isn’t registered, or the visitor cancels, the assistant gets an error result instead. See Errors.
Return only what the assistant needs to answer. Small, flat results with clear field names work best. If you cap a list, say so in the result (for example { "items": [...], "returned": 20, "total": 137 }).
Show a result card
Section titled “Show a result card”After a visitor approves an action, your handler can also return a small confirmation card for the visitor. Wrap the result in an envelope marked result/v1:
agentMySite.registerToolHandler(async function (name, args) { if (name === 'cancel_order') { // ...cancel the order on your server... return { __agentmysite: 'result/v1', data: { cancelled: true, orderId: args.orderId }, // what the assistant sees card: { title: 'Order cancelled', rows: [['Order', args.orderId], ['Refund', '3–5 business days']], reference: args.orderId, }, }; }});datais what the assistant sees. If you leave it out, the assistant sees the whole object withoutcard.cardis shown to the visitor under the step row. It’s optional.- Limits: a title of up to 80 characters, up to 6 rows (label up to 40, value up to 120), and a reference of up to 40. Longer text is cut and extra rows are dropped. Numbers are turned into text, and other values are dropped.
- Everything is shown as plain text. Markup and links aren’t rendered.
- No card is shown when your handler throws or returns an error.
- Any other result works as before. Without the
__agentmysite: 'result/v1'marker, the object is treated as an ordinary result.
The card is a plain object, so there’s nothing extra to load. The stored tool result keeps exactly what your handler returned.
Limits
Section titled “Limits”| Name | Default | Description |
|---|---|---|
Handler timeout | 15 seconds | After this, the assistant gets a timeout error. For slow jobs, start the job and return a status straight away. |
Handlers per page | 1 | Registering again replaces the previous handler. Use one handler that switches on name. |
Tool name | 64 characters | Letters, digits, _ and -. |
Credits | 1 per tool call | Each tool call the assistant makes uses one credit, whatever the outcome (completed, failed, or cancelled). The assistant’s replies use credits as usual. |
Chat rate limit | 20 per 10 minutes per visitor | Sending a tool result back to the assistant counts as a message toward the visitor’s chat rate limit. |
What AgentMySite stores
Section titled “What AgentMySite stores”Tool calls are part of the conversation, stored as tool_use and tool_result content blocks alongside the visitor’s messages, so the conversation can continue where it left off. The tool result your handler returns is stored with the conversation.
The Audit page in the admin panel lists each tool call with its arguments and outcome. Tool results aren’t shown there. Don’t return data the assistant doesn’t need, especially personal data.
Security checklist
Section titled “Security checklist”- Authenticate on your server. The handler runs in the visitor’s browser. Your backend must check the user’s session and permissions, just as it would for a request from your own front end.
- Never put secrets in the handler. API keys belong on your server. The handler should only forward
nameandargsto your backend. - Keep error messages safe. The text of a thrown
Errorgoes to the assistant and can reach the visitor. Leave out stack traces, tokens, and internal IDs. - Make mutating actions safe to repeat. The assistant can call a tool more than once. Use idempotent operations where you can, and rate-limit sensitive routes.
- Reject unknown tool names. Keep a
defaultbranch that throws. - Only enable tools you’ve implemented. An enabled tool without a
casein your handler fails each time it’s called.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Likely cause | Fix |
|---|---|---|
| The assistant says no tool handler is registered | Your code ran before the widget script loaded, or it never ran | Register through onAgentMySiteReady (above), after the widget script tag. |
| The assistant never calls the tool | The tool is disabled, or its description doesn’t match what visitors ask | Enable the tool, and say clearly in the description when to use it. |
| The assistant says the parameters are invalid | The arguments don’t match the schema | Add clear descriptions and examples to the schema properties, and loosen anything too strict. |
| The tool times out | Your handler took more than 15 seconds | Return sooner. Start long jobs in the background. |
| Your backend returns 401 or redirects to sign-in | The session cookie wasn’t sent | Use credentials: 'include', and call a route on your own site’s domain. |
| The confirmation card doesn’t appear | The tool isn’t mutating, or Require confirmation is Never | Turn on Mutating, or set Require confirmation to Always. |
Was this page helpful?
Thanks for the feedback.