Skip to main content
In-app tools let your AI agent use your own website or mobile app during a chat. Your website or app declares what the agent may do, such as listing the customer’s devices, opening a page or removing a device, and supplies the code that does it. The agent calls a tool, your code runs in the customer’s browser or app, and the result goes back to the agent. The code runs inside the customer’s signed-in session. It calls your own API as the customer, with the same permissions the customer already has.

How it works

1

Your app declares its tools

Each tool has a name, a description the agent reads, a JSON Schema for its arguments, and a kind: read, safe action or action. You register the tools with the web widget or the mobile SDK, together with the function that runs each one.
2

The agent is offered them

While the customer chats, the AI agent is offered the tools this customer’s app declared, when the channel has Use for chat on. On a channel with it off, the agent never sees them.
3

The agent calls a tool

A read runs at once, whenever the agent needs the data. A safe action runs at once too, but only when the customer asks for it. An action shows the customer a consent card first, and runs only after they tap Allow.
4

The result goes back to the agent

Your function’s return value reaches the agent, which uses it in its next reply. The conversation keeps a record of every call, visible to the customer and to your team in the inbox.

Turn it on for a channel

App tools are set per channel, in one place, for the chat and for voice calls. They are off by default: on every channel, a website’s or an Android or iOS app’s, Use for chat and Use for voice start off, and the agent gets no in-app tools until an admin turns Use for chat on. To turn them on:
  1. In the cabinet, open the channel: Integrations > Web for a website, or Integrations > Mobile for an Android or iOS app. A Flutter app has an Android and an iOS channel: turn the switches on for both.
  2. Under In-app tools, turn on Use for chat: it lets the AI agent that answers the chats of this channel use the tools your website or app registers. Once it is on, the ready-made tools work too, all groups by default, and your developers turn groups off in code. To let the voice assistant use them during a call too, turn on Use for voice (see In a voice call). Use for voice needs Use for chat, because the voice assistant works inside the chat: turning chat off turns voice off too.
  3. Click Save changes.
Clarity checks the switch on its side: a chat on a channel with Use for chat off gets no app tools, whatever the page registers.
The tools your developers register work on every channel. The ready-made tools need Android SDK 1.9.0 or ClaritySDK 1.6.0 for iOS in an app: the SDK version decides, not a cabinet setting.

Choose the kind

The kind says what a tool changes for the customer, and so when the agent may call it: For example, “Which devices are on my account?” makes the agent call list_devices and answer in the chat. “Show me my devices” opens the devices page with a safe_action. “Sign out the TV” puts a consent card in front of the customer before remove_device runs.

Declare your tools

Register the tools before the customer opens the chat, for example at startup. Registering the same name again replaces the tool in its place. The agent is offered your tools in the order you register them, and it reaches for the ones listed first: register the tools it should use most, first. If a customer has the chat open in more than one place, such as two browsers or your website and your app, the agent uses the tools of the chat they are writing in, and its calls run there. The web widget adds window.anecdoteLiveChatRegisterTool once its script has loaded. Call it from the snippet’s onload, after anecdoteLiveChatInit, or from window.onAnecdoteSuccess.
execute may return a value or a promise. A thrown error reaches the agent as the tool’s error message. anecdoteLiveChatRegisterTool returns false and logs the reason to the browser console when a declaration is invalid. window.anecdoteLiveChatUnregisterTool(name) removes a tool.
On a single-page app, open pages through your router (as router.push above) rather than with a full page load. The chat then stays open, and the customer sees the agent’s reply right away.

Argument and result schemas

Schemas use a subset of JSON Schema that every model provider accepts:
  • Keywords: type, properties, required, items, enum and description.
  • Types: object, array, string, number, integer and boolean.
  • At most 4 levels of nesting, 32 properties per object and 50 enum values.
  • A description inside a schema is at most 300 characters, and an enum value at most 100.
  • Property names follow the same rule as tool names.
  • confirmation_text is reserved for the consent card and cannot be an argument.
The agent’s arguments are checked against the schema before your code runs.

Limits

A tool that breaks a rule is left out, and the others are still offered. The web widget logs the reason to the browser console.

Ready-made tools

On a website, the web widget registers common tools for you, with no code: every group in the table below is on. It does so only once an admin has turned Use for chat on for the channel in the cabinet (see Turn it on for a channel); both switches are off by default, and with both off the widget registers no built-in tool. Which groups are on is set in your site’s code, not in the cabinet, so your developers keep control of it. Add inAppToolTemplates to the o object of the widget snippet:
  • Nothing passed: all seven groups are on.
  • { location: false, ... }: those groups are off, the rest stay on.
  • {} or false: all built-in tools are off.
A group can also take an object of its options (see Options), which keeps it on. Your own tools work whichever you choose.
The Android SDK (since 1.9.0) and the iOS SDK (since 1.6.0) offer the same groups and tools for the screen of the app the customer is on, set in ClarityConfig.inAppToolTemplates by the same rules. There, siteSearch needs your website’s address and navigation the app’s screens before they offer anything. See Ready-made tools for Android and Ready-made tools for iOS. The Flutter plugin (since 0.6.0) offers siteSearch, navigation, diagnostics and, on Android, location; see Ready-made tools. The kind of a tool decides whether the customer is asked first. A read changes nothing on screen and runs at once. A safe action (open a page, point at something, fill a field) runs at once, because the customer can see it and undo it. Important actions (kind action, such as pressing a button with click_element) run only after the customer allows them: they wait until the customer taps Allow on the card, or says yes in a call. get_location then also gets the browser’s own permission prompt. To give the agent a tool of your own, register it with window.anecdoteLiveChatRegisterTool (see Declare your tools). It works with or without the ready-made groups. The default (every group on) makes 13 tools, so there is room for your own: a page offers at most 32. Your own tools always come first, so if the total is over 32 the ready-made ones that do not fit are left out; if your own tools alone are more than 32, the ones past 32 are left out too. Either way the widget names what it left out in the browser console.

Options

true turns a group on with its defaults. An object turns it on with options; {} is the same as true:

Opening pages

open_link opens a page of your site in one of three ways, set with navigation.mode: While a page opens, the page dims slightly and a soft sheen passes over it, so the customer sees the agent is doing it. In handler mode, register how your app navigates once its router exists. A returned promise is awaited, and an error reaches the agent:
Or handle the anecdote-live-chat:navigate event, and call done() or fail(reason) when the page has opened or could not:
If your app does neither, open_link tells the agent the site has no handler. Your app has 10 seconds to open the page.

Sites drawn by JavaScript

The page tools read the page as the browser shows it, so they work the same on a page your server renders and on one a JavaScript app (React, Vue, Angular) draws. read_link and find_on_site read other pages by fetching them, and a fetch runs no scripts. On a single-page app that HTML is an empty shell, so:
  • read_link tells the agent the page cannot be read without opening it.
  • find_on_site without words reads your sitemap (/sitemap.xml, or the sitemaps robots.txt names), a static file even on a single-page app, so the agent learns which pages your site has.
  • find_on_site with words uses your site’s own search when it can read the results. It takes, in order, the searchUrl option, an OpenSearch description the page links to, or a schema.org SearchAction. When there is none, or its results are drawn by JavaScript, it matches the words against the page addresses of your sitemap.
Publishing a sitemap with descriptive addresses (/help/return-policy) is what helps the agent most on a single-page app. The tools stay within the limits of the browser and of your site:
  • Your site only. read_link, find_on_site and open_link reach pages of the same origin as the page. Other sites are refused, and so is a page of yours that redirects to another site.
  • Public pages only. read_link and find_on_site fetch a page without the customer’s cookies, so the agent reads what a signed-out visitor sees and never the customer’s own account pages.
  • What read_page returns without asking. The page’s title, its main heading, the text the customer selected, the text of its links and menu, the names of its forms, fields and buttons, and the error alerts it shows (role="alert", role="alertdialog", aria-live="assertive"). On an account page that text can hold the customer’s name or an order number (“Hi, Jane”, “Order #1234”). No tool reads what they typed or picked in a field.
  • The rest of the screen only with consent. get_page_text reads the page’s whole visible text, account details included, and asks the customer first every time: dialogs, status lines, toasts and banners are part of that text, not of read_page. It never reads what they typed or picked in a field, hidden elements or scripts. With page.pageText: false it is left out.
  • Every field seen and filled, never read. get_form lists every field of the page’s forms the customer sees: the standard ones (a name, an organization, an email, a phone number, an address) by their autocomplete type, and the others (a date of birth, a free-text reason, a choice) by a field name taken from their label, with their kind, whether they are required and filled, and the choices a select or radio group offers. The agent fills any of them with what the customer says: it types a text or a date, picks a select option or a radio by the words the customer sees, and ticks or unticks a checkbox. It never returns what is typed or which choice is picked, never picks a file for the customer, and leaves out what is inside a private part (data-clarity-mask, data-clarity-exclude).
  • Errors as your page shows them. get_form reads the conventions forms already use: aria-invalid with aria-errormessage or aria-describedby, the browser’s own validation (:user-invalid), and the error classes of common UI kits (Bootstrap, Material UI, Ant Design, Chakra). read_page reads role="alert" and role="status", live regions, open dialogs and toasts. Marking your errors this way, which accessibility asks for anyway, is all it needs.
  • One field at a time. The agent focuses a field (the page scrolls to it, it pulses twice and stays highlighted in your widget’s primary colour), asks for its value and fills it, then moves to the next. The customer can also type into the highlighted field and tell the agent when done. Several values given at once are filled together. fill_form_fields takes one field flat (field and value), the shape a light voice model manages, or several in a fields list; a call with neither or both is refused. The highlight goes when the field is filled, when the customer turns to something else, or after two quiet minutes. In a voice call the agent spells an email or a phone number back before filling it.
A tool you register yourself takes precedence over a ready-made one of the same name, so you can replace any of them. Calling anecdoteLiveChatInit again applies its own inAppToolTemplates by the same rule: with nothing passed, all seven groups are on again.

Pressing for the customer

click_element presses an element by the name the customer sees, the same names read_page lists and show_on_page points at. It is an action, so it runs only after the customer taps Allow on the card (its line names the element, such as Press “Save profile”), or says yes in a voice call. The page scrolls to the element, which is checked again right before the press, and then pressed with a click, the same event your page’s code gets from a real click. The agent gets back pressed: true and the element’s label, and the new url when the address changed (a router that draws the next page). A press that loads another page (a plain link, or a form your page lets the browser send) is held until the result has reached the agent, then made, and the chat opens again on the new page, as with open_link in reload mode. During a voice call such a press is refused, because the load would end the call. It presses nothing, and tells the agent to point at the element with show_on_page and ask the customer to press it themselves, when the element:
  • is not on the page (the agent is then told the names it can use), is hidden, or is not a button, link or other control;
  • is disabled (disabled, or aria-disabled="true");
  • is a field to type in (a text input, a text area, a select): the agent is told to use focus_form_field or fill_form_fields instead;
  • picks a file (input type="file");
  • opens a new window (target="_blank" on a link or a form) or another app (mailto:, tel:), which browsers allow only from a real press;
  • leads to another site, or sends its form to one;
  • is inside a frame, which the page cannot reach;
  • is inside an element marked private with data-clarity-mask or data-clarity-exclude;
  • carries data-clarity-no-click, or sits inside an element that does.
Mark anything the agent must never press, such as “Delete account” or a payment button, with data-clarity-no-click:
To turn pressing off on the whole site, pass "inAppToolTemplates": { "click": false }.

What the customer sees

  • Read: while the tool runs, the chat shows a short “checking in the app” status.
  • Safe action: it runs at once, and the change shows on the page.
  • Action: the chat shows a consent card. Each step on it is one sentence the agent writes, such as “Sign out the living room TV”. Under it are the tool’s name and the exact arguments, which the agent cannot word. The customer taps Allow or Decline.
The card is the question: the agent does not ask in words first and wait for a typed yes. Its reply next to the card is one short sentence asking the customer to allow it, and it answers right away anything they asked that does not depend on the action. The card stays until the customer answers, even across a page reload. If the customer writes a new message instead, the card closes as Ignored and can no longer be allowed. When the customer allows the action, the card shows the outcome: done, failed, or not finished in time. The agent then continues the conversation with the result. When the action was needed to answer the customer’s question, the agent answers that question: “Is there a store in my city?”, the customer allows get_location, and the reply names the nearest store. A plain change, such as a new phone number, gets a short confirmation. The card belongs to the conversation the agent asked in. When one request takes several actions, such as “add the Kids pack” and “add the Cinema pack”, the customer gets one card that lists every step. One Allow runs the steps one after another, in the order the agent asked for them. If a step fails, the steps after it do not run. One Decline declines them all. A safe action runs at once, without a card. If the agent has already put a card in front of the customer in the same reply, the safe action joins that card as a step, so the steps keep their order. Write descriptions that say when the agent should use each tool. An action or a safe action changes something for the customer and returns nothing for the agent to read, so the agent calls it only when the customer asks. To let the agent look something up, give it a read tool. For example, add a read tool that reads a page without opening it, next to a safe action that opens the page. Every call leaves a line in the conversation with the tool and its outcome. Your team sees the same record in the ticket.

In a mobile app, the chat steps aside

In the iOS and Android SDKs the chat is a sheet over your app, so a tool that opens a screen would do it out of sight. Give such a tool a reveal: the chat steps aside with a loader while the tool runs, the customer sees the screen open, and the chat comes back. See Show what the tool did in the iOS and Android guides, and In-app tools in the Flutter guide.

An Android app opens the plan screen while the chat steps aside

In a mobile app, confirm once on the device

An app can mark an action as needing its own security check, such as Face ID, a fingerprint or a PIN. When the customer taps Allow on a card with such an action, the app runs the check once for the whole card, before anything runs: a card of three steps asks once. If the check does not pass, nothing runs and the card asks again. See Confirm once in the iOS and Android guides; the Flutter plugin has the same requiresSecurityCheck and Clarity.setSecurityCheck.

In a voice call

When your widget offers voice calling, the voice assistant can use the same tools during a call. A customer can say “show me my devices” or “sign out the TV” and the assistant does it in your website for them, and says what it found or what changed. This also helps a customer who cannot easily see or use the screen.
  1. In the cabinet, open the channel’s settings, where voice calling is turned on.
  2. Under In-app tools, turn on Use for chat and Use for voice. Voice is greyed out while chat is off.
  3. Click Save changes.
The call can use the tools your website declared when the call started, and runs them only in the chat that placed the call. The kinds work as in a chat:
  • Read: the assistant looks the data up and tells the customer the answer.
  • Safe action: it runs at once, for example the devices page opens behind the call.
  • Action: the call steps up and makes room for a short confirmation with Allow and Decline, with Allow focused, so a keyboard or screen reader user can answer with one key, and the assistant says once what the action will do and asks whether to go ahead. The confirmation lists each step on its own line. The customer can tap Allow, or just say yes. Only a clear yes said once the confirmation is up counts: a question, a hesitation, the request that led to the action or a yes said before never allows it. All the actions of one request share one confirmation. When they answer, the assistant tells them the outcome. A confirmation still waiting when the call ends is declined.
In a call the customer cannot read an answer on the screen, so when they ask to see something, the assistant shows it with your tools: it opens the page, sets the filter they asked for, or scrolls to it, and says in a sentence what is there. Give it the tools to do that. Declare a filter as one safe_action that chooses the value and applies it, rather than one tool to choose and another to press the apply button. If the chat is open in several tabs, only the tab the call is in runs the action. The conversation keeps the same record of every tool call as in a chat, marked In a voice call. The records reach the chat’s ticket when the call ends, with the call’s transcript, each at the time it was made, so a call placed before the customer wrote anything is recorded too. When the customer answered out loud, the record says Allowed by voice or Declined by voice, with the words they said.

In a mobile app, the call folds to a bubble

On a website the call collapses to a bubble over your page in the same way: when the assistant points at something with show_on_page, a call in the panel collapses, and the bubble moves off what is pointed at. See Collapse the call to a bubble. On a website, a card opens a call that is a bubble, so the customer can answer it. Once they allow it, the call goes back to the bubble when every step on the card has collapseCallOnAllow, and your page is in view while the steps run. The ready-made actions have it unless you turn it off with <group>.collapseCallOnAllow: false. A call the customer opened themselves stays open: only the call the card opened goes back, and only for that card’s Allow. In the iOS and Android SDKs a call can collapse to a bubble over your app, so the customer keeps using the app while they talk. During a call in the sheet, a tool with a reveal collapses the call this way instead of stepping the chat aside, and the customer sees the screen open; a full-screen chat stays as it is, and the screen opens behind the call. An action that shows its result in your app can fold the call too: register it with collapseCallOnAllow, and once the customer allows the card, the call folds back to its bubble while the steps run. The bubble comes back as the call when the assistant needs another answer on screen. See Voice calls in the iOS and Android guides.

An Android app: the customer allows a purchase, and the call folds back to its bubble

Security

Declarations, arguments and results all come from the customer’s device, so treat them like any other input from a browser or app:
  • Authorize in your API. A tool can do only what your API lets this customer do. Check every change on your server, as you would for the customer clicking the button themselves.
  • Keep it least-privilege. Expose what a support conversation needs. Make anything that changes the customer’s data, money or settings an action, so the customer confirms it. Keep safe_action for what only changes the screen.
  • The agent treats app data as data. Clarity marks every result and every error from your app as untrusted before the agent sees it, and the agent is told never to follow instructions inside it. A description is shown to the agent as your app’s description of the tool, capped in length, and an app tool cannot replace one of the agent’s own tools.
  • Nothing is unlocked by a result. A result never grants the agent access to anything on the Clarity side, such as a tool your team configured or another customer’s data.
  • The kind you register is the kind that runs. Clarity refuses a call below the kind your page or app registered, so a declaration altered on its way cannot turn an action into a read and skip the customer’s Allow.
  • One Allow covers the steps it showed. Allow starts only the steps the card showed when the customer tapped it. A step that reached the card later asks again on a card of its own.
  • Only your page and the chat reach your tools. The web widget takes tools and events only from the page that embeds it. The Android SDK (since 1.9.0) and the iOS SDK (since 1.6.0) do the same: only the chat page itself reaches your app, and a script inside a frame of the chat cannot call your tools.
  • Logs keep out the data. Clarity logs the tool name and the call id, never the arguments, the result, or the chat’s session.

Troubleshooting