#An AI chat for your website
To put an AI chat on your website with Vatio, you publish your agent, create a publishable vatpub_ token that only works from your site’s origin, and paste one script tag. The widget takes its look from Vatio’s inbox, and any page can override it; the business name and description the agent works with are set on the inbox’s Business page. When your backend signs a token saying who a visitor is, the same widget recognizes signed-in customers and runs the tools that read their own data. It is the same agent that answers on WhatsApp and Instagram, described in one vatio.yml.
This guide goes from a published agent to a widget on your site: themed, aware of who is signed in, and tried on preview first.
#Start from a published agent
The widget talks to one environment, normally live. If you have no agent yet, Build an AI agent gets you one; then:
vatio push
vatio publish
Before you publish, try it on the inbox’s Test page, the link vatio push prints after Try it:. The widget needs a site to sit on; if you have no website, or not yet, the same agent can answer your customers on WhatsApp or Instagram instead.
#Create a token for your site
vatio tokens create --env live --label website \
--origin https://ember-roasters.example --origin http://localhost:3000
create prints the full token once. A publishable token works only from its allowed origins, plus Vatio’s own pages and the app builders: every token accepts Lovable, Replit, Bolt, v0 and Base44 previews and published apps without listing them, so an app built there works as soon as the snippet is in. Origins are exact scheme://host[:port] values with no path or wildcard, so https://www.ember-roasters.example is a second origin to list if your site answers there too. A page on an origin the token does not accept gets origin_not_allowed, and the browser console names that origin, ready to paste.
vatio tokens origins PREFIX URL... replaces a token’s list, and the tokens section of Channels → Web widget in the inbox edits it too. vatio tokens list shows prefixes and origins; vatio tokens revoke PREFIX stops new chats with that token, while chat credentials already issued stay valid until they expire.
A publishable token selects one workspace and one environment, and it cannot deploy anything or open the inbox: it is made for page source. Never put a vat_ developer token in a browser. And the origins keep a token on your site; they say nothing about who the visitor is. That is the visitor token’s job, below.
#Paste one script tag
<script async src="https://cdn.vatio.ai/v1/widget.js"
data-workspace="ember-roasters"
data-token="vatpub_REPLACE_ME"></script>
Put it before </body> in the layout every page shares. The widget waits for the page to load and go idle before starting, so it does not compete with your own content; data-eager="true" starts it immediately. Channels → Web widget also shows a ready install snippet whose token Vatio manages; edit that token’s origins in the page’s tokens section.
If your app has a bundler (React, Next.js, Vue, Svelte), you can install the same widget from npm instead and mount it from your own code: npm install @vatio-ai/widget, then mountVatioWidget({ workspace, token }), which returns identify and unmount. The options are the same as the data-* attributes, in camelCase; see From your app’s code. The widget is open source (MIT) at github.com/vatio-ai/widget.
On the web, each reply is one complete message that the widget renders progressively, in Markdown, so lists, bold text and links display as they should. WhatsApp and Instagram get the same agent’s replies as paced plain-text messages instead. The agent only sends URLs it was given, and when a request looks resolved, the widget asks the visitor once how the agent helped; the rating shows in the inbox.
#Make it look like your brand
In the inbox, open Channels → Web widget. It holds the logo, which is the agent’s avatar in the widget and on the chat page; the accent color; About, the text visitors see when the widget opens, before anyone writes; the title, greeting, up to four suggestions, position, color scheme, corner radius, font, language and more colors.
The business name and its Description, which goes into the agent’s prompt around its instructions, are not here: whoever runs the business sets them on the Business page of the inbox. This is how the agent knows what the business is.
These are workspace settings, not part of a deployment: a save applies to every environment immediately, with nothing to push. A widget: or business: block in an older vatio.yml is ignored with a warning.
A page can override any of the widget’s settings on its own tag; values resolve from the page, then the workspace, then the platform default. On the subscription pages, for example:
<script async src="https://cdn.vatio.ai/v1/widget.js"
data-workspace="ember-roasters"
data-token="vatpub_REPLACE_ME"
data-greeting="Questions about your subscription? Ask away."
data-suggestions="When is my next delivery?|Which coffee ships this month?|How do I brew a pour-over?"
data-accent="#8A3B12"
data-position="left"></script>
The attributes are data-accent and data-accent-ink, data-surface, data-ink, data-muted and data-line (all #RRGGBB), data-scheme (auto, light or dark), data-position (right or left), data-reply-style (stream, or paced for short messages with a typing indicator, as on WhatsApp), data-font (inherit uses your site’s font), data-radius, data-title, data-greeting, data-suggestions (pipe-separated, up to four), data-about and data-locale (en, es or pt). data-locale sets the interface labels; the agent replies in the visitor’s language either way.
#Recognize signed-in customers
To tell the agent who is chatting, your backend signs a JWT and Vatio verifies it. Vatio holds only your public key, so it can check a token but never mint one.
vatio auth --new-key
identity.pub stays in the workspace and vatio.yml names it under auth. identity.pem goes into your backend as a secret, never into vatio secrets set. Your backend signs with RS256, with sub (your user id), aud (your workspace slug) and a short exp; name and email are optional:
// Node (jsonwebtoken)
jwt.sign(
{ sub: String(user.id), name: user.name, email: user.email },
process.env.VATIO_IDENTITY_PRIVATE_KEY,
{ algorithm: "RS256", audience: "ember-roasters", expiresIn: "1h" }
);
Render it onto the tag for signed-in visitors only:
<script async src="https://cdn.vatio.ai/v1/widget.js"
data-workspace="ember-roasters"
data-token="vatpub_REPLACE_ME"
data-visitor-token="SIGNED_JWT"></script>
Signed-out visitors get no token, and the agent answers them with public tools. When someone signs in without a reload, call window.VatioWidget.identify(newToken); null signs them out. The conversation survives the sign-in: a question asked anonymously carries over, now with a name on it, so the agent can answer it without the customer typing it again. A token with a different sub, or signing out, starts a new conversation, so a shared laptop never shows one customer another’s chat. Render a fresh token on each page load to outlive the first exp.
With a verified visitor, tools marked access: private run. tools/my_subscription.yml, with the API address stored by vatio secrets set EMBER_API:
description: The signed-in customer's subscription and next delivery.
when_to_use: A signed-in customer asks about their own subscription, next box or delivery date.
access: private
request:
method: GET
base_url: "$env.EMBER_API"
path: /me/subscription
headers:
Authorization: "Bearer $auth.token"
respond:
data:
subscription: "$.subscription"
agent:
instructions: |
...
Subscription questions: call my_subscription. If the visitor is not
signed in, send them the sign_in link and answer once they are back.
tools: [my_subscription, knowledge_lookup]
links:
sign_in: https://ember-roasters.example/login
auth:
public_key: identity.pub
Your endpoint verifies the same JWT with the public key and reads the subscription of its sub. A private tool never takes the user’s id as a parameter, and the deploy fails if one does: the model fills parameters and could be talked into filling in someone else’s. A rejected token leaves the visitor anonymous instead of failing. Connect your backend goes deeper.
#Try changes on preview first
Preview is the next version of the agent, with its own knowledge changes, chats and publishable tokens. After vatio push, vatio env prints preview’s token, widget snippet and test link. vatio tokens create --env preview --origin https://staging.ember-roasters.example creates a token for your staging site on preview; for local work, use --origin http://localhost:3000.
The look is the exception: the settings in Channels → Web widget are workspace-wide and apply everywhere the moment you save. To try a new look on staging only, use data-* attributes there.
#Your own interface with the browser SDK
When the chat is part of your product rather than a bubble in the corner, use the browser SDK with the same token and origins:
npm install @vatio-ai/sdk
import { Vatio } from "@vatio-ai/sdk";
const chat = await Vatio.chat({ workspace: "ember-roasters", token: "vatpub_REPLACE_ME" });
chat.on("message", (message) => render(message)); // dedupe by message.id
chat.on("typing", (typing) => showTyping(typing));
for (const message of await chat.history()) render(message);
await chat.send("When is my next delivery?");
visitorToken takes the same signed JWT as data-visitor-token. replyStyle defaults to stream, one complete reply to render progressively, and paced delivers short messages with typing indicators instead. chat.send accepts up to four files: images, PDFs, text or audio. The SDK reference lists every method and option.
#Watch it from the inbox
Every conversation, from the widget or the SDK, shows in the inbox with its tool calls. Add a metadata object to the visitor token, with a profile link to your admin and a few badges such as the customer’s plan, and the inbox shows them in the conversation header, so whoever answers knows who they are talking to. Supervisors take a conversation over by replying, and flag wrong replies with what should have happened; your coding agent fixes the cause from those notes, as Improve your agent explains.
Frequently asked questions
How do I add an AI chat widget to my website?
Publish your agent, create a publishable token that allows your site's origin with vatio tokens create, and paste one script tag carrying data-workspace and data-token before the closing body tag. The widget's look is set in Channels → Web widget in the Vatio inbox, and the business name and description on the inbox's Business page.
Is it safe to put the chat widget's token in my page's HTML?
Yes, publishable vatpub_ tokens are made for page source. Each one works only from the exact origins listed on it, plus Vatio's own pages and app builders like Lovable, and it cannot deploy anything or open the inbox. Never put a vat_ developer token in a browser.
Can the AI chat recognize customers who are signed in to my site?
Yes. Your backend signs a short-lived RS256 JWT saying who the visitor is and renders it as data-visitor-token on the script tag. Vatio verifies it with your public key, and tools marked access: private then run for that customer. A conversation started before signing in carries over.
Can I change the widget's colors and text on a specific page?
Yes. Shared defaults live in Channels → Web widget in the inbox and apply to every environment as soon as you save. Any page can override them with attributes on its own script tag, such as data-accent, data-greeting or data-suggestions.
Do I need a website to offer the AI chat?
For the widget, yes: it is a script tag on your own site. Without one, the same agent can still answer your customers on WhatsApp or Instagram, and you try it on the inbox's Test page before anyone else sees it.
Can I build my own chat interface instead of using the widget?
Yes. The browser SDK, @vatio-ai/sdk on npm, gives you the conversation, its history and typing and status events with the same publishable token, and you draw the interface yourself.
Updated: