On this page

#An AI chat for your web app or site

The web widget puts your agent's chat in your web app or site with one script tag, or the widget's npm package, and a publishable vatpub_ key that only works from your app's origins. This guide is the task path: what to ask for, what you will see, how to try it and what to check before going live. The reference for the key, origins and every setting is Web widget. It is the same agent that answers in your iOS, Android or Flutter app.

#Start from an agent

The widget talks to one environment, normally live. The app is the starting point:

  1. Fastest, from any chat. Add the remote MCP server https://vatio.ai/mcp as a connector in Claude, ChatGPT or Cursor, sign in with a Vatio account and ask for the agent. It is created in a preview to try; nothing reaches users until you publish (Vatio in Claude and ChatGPT).
  2. To build the agent into your app's code. In the project run npx @vatio-ai/skills, then /vatio-init, then ask your coding agent to add the web widget, and /vatio-improve daily. Build an AI agent walks through it.

Before anyone else sees the agent, try the preview with Try the agent in the inbox. Publishing is your decision: when it is right, ask to publish.

#Create a publishable key and paste the tag

Ask your coding agent to create the key for your app's origins, or run it yourself:

Terminal window
vatio keys create publishable --env live --label website \
--origin https://ember-roasters.example --origin http://localhost:3000

create prints the full key once. Origins are exact scheme://host[:port] values, so https://www.ember-roasters.example is a second origin if your site answers there too. Apps built in Lovable, Replit, Bolt, v0 and Base44 work without listing their addresses. Never put a vat_ developer token in a browser. Key rules, origin_not_allowed, listing and revoking are in Web widget.

Paste the tag before </body> in the layout every page shares:

<script async src="https://cdn.vatio.ai/v1/widget.js"
data-workspace="ember-roasters"
data-token="vatpub_REPLACE_ME"></script>

With a bundler (React, Next.js, Vue, Svelte) install it from npm instead: see From your app's code.

On the web each reply is one message the widget renders progressively, in Markdown. When a request looks resolved, the widget asks the visitor once how the agent helped (Visitor feedback).

#Make it look like your brand

In the inbox, open Web chat. It holds the title, greeting, up to four suggestions, position, color scheme, corner radius, font, language and the theme colors.

The Name and brand card of the same page holds the business name, logo, brand color and About; what the business does goes in the agent's instructions. These are workspace settings: a save applies to every environment immediately, with nothing to push.

A page can override a setting on its own tag. 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>

Every attribute is in the table under Widget configuration.

#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. If your users sign in with Supabase Auth, as apps built with Lovable often do, an Edge Function can read their session and sign it (Supabase apps). Your coding agent creates a key pair:

Terminal window
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. See Authentication and Tools.

#Try changes on preview first

Preview is the next version of the agent, with its own knowledge changes, chats and publishable keys. After vatio push, vatio env prints preview's key, widget snippet and test link. vatio keys create publishable --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 Web chat in the inbox 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 key and origins:

Terminal window
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 report wrong replies with Report this reply, writing what should have happened. Running /vatio-improve in your coding agent fixes the cause, pushes preview and tests the reported cases; you approve publication, as Improve explains.

#Frequently asked questions

#How do I add an AI chat widget to my web app or site?

Once your agent is published, your coding agent creates a publishable key that allows your app's origin, with vatio keys create publishable --origin or in Settings → API keys in the Vatio inbox, and pastes one script tag carrying data-workspace and data-token before the closing body tag, or mounts the @vatio-ai/widget package. The widget's look, with the business name, logo and brand color, is set on the inbox's Web chat page.

#Is it safe to put the chat widget's key in my page's HTML?

Yes, publishable vatpub_ keys 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 the inbox, in Web chat, brand color included, 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.

#Does the widget work in an app built with Lovable, Replit or Bolt?

Yes. Every publishable key accepts the preview and published addresses of Lovable, Replit, Bolt, v0 and Base44 without listing them, so the widget works there as soon as the snippet is in. Once the app moves to a domain of its own, add that origin to the key. For an iPhone, Android or Flutter app, use the iOS, Android or Flutter SDK instead.

#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 key, and you draw the interface yourself.

Last updated:

Latest CLI version: 0.114.0 · Changelog · The whole contract as Markdown: /docs/docs.md · or vatio docs