#Build a Vatio agent: the playbook for coding agents
This page is written for the coding agent, Claude Code, Codex, Cursor or any other, that a developer has asked to build a customer-facing AI agent on Vatio. It is the order that works: what to look at, what to ask the owner, what to write, how to test it and what to hand over, with one complete example. People are welcome to read it; the developer’s version is shorter.
The contract, every field of vatio.yml and every command, is in the docs, and the docs win wherever they disagree with this page. Read them before you write anything:
npx @vatio-ai/cli docs
The same is one Markdown file at vatio.ai/docs/docs.md, and every docs page is also at its own address plus .md. This page is at vatio.ai/guides/coding-agent-playbook.md.
#What you are building
- One workspace, one agent. A workspace has a slug and runs exactly one agent, described in one
vatio.ymlwhose only root keys areworkspace,agentandauth. - Instructions, tools and knowledge. The instructions say what the agent does and the rules it never breaks. A tool is one HTTP request to the business’s API, described in YAML under
tools/. Knowledge is Markdown entries that the agent searches, stored in Vatio and versioned. - Environments.
vatio pushdeploys topreview;vatio publishtakes it tolive. Nothing reaches customers until it is published, and until the first publish, preview is the agent. - No model to pick. Vatio runs the model and the conversations. There is no model key and no model setting.
- Channels. The same agent answers on a website widget, WhatsApp, Instagram DMs and the browser, iOS and Android SDKs. Supervisors read every conversation in the inbox, take one over by replying, and flag wrong replies.
What it does not do, so you never promise it to the owner:
- It replies in text. It reads images, voice notes and PDFs, but it sends no files.
- It does not read websites. What it should know, you write as knowledge.
- It writes first only when the business’s backend calls the Messages API, on WhatsApp. It never decides on its own to write to someone.
- Vatio does not notify anyone of a handoff yet: the conversation shows as waiting in the inbox.
#The order that works
- Install the skills.
- Look before you create.
- Say what you found, and recommend.
- Ask the owner only what the code does not answer.
- Write the agent:
vatio.yml, tools and knowledge. - Check, push and try it.
- Hand over the link.
- Publish only when asked.
- Before you finish, report what got in the way.
#1. Install the skills
Unless .agents/skills/vatio-init is already in the project, run this from its root:
npx @vatio-ai/skills
It copies the Vatio skills into .agents/skills/ and .claude/skills/, so this procedure and the one for improving the agent are in the repository for the next session, whichever coding agent runs it. vatio below is npx @vatio-ai/cli when the CLI is not installed globally. It needs Node.js 20 or newer.
#2. Look before you create
The project may already have an agent.
- Search for a
vatio.ymlat or above the current directory and anywhere in the repository:git ls-files '*vatio.yml'. Itsworkspace:line names the workspace. If there is one, the agent exists: never runvatio initthere, because it writes a starter agent and pushing that replaces the real one. Follow Joining an existing workspace instead. - A workspace slug but no
vatio.yml: ask for the repository it lives in. Vatio keeps what was deployed, not the files it came from. - Read the app. What it does and for whom; the API an agent could call (routes, controllers, an OpenAPI file); how its users sign in; and the docs, FAQs and policies an agent could read.
- Check the machine.
vatio doctorsays whether the CLI is signed in and which workspace this directory resolves to.
#3. Say what you found, and recommend
Before changing anything, tell the person you work for, in a few lines, what is there and what you recommend, and ask them to confirm. Make the proposal concrete, from their code:
This is a bike shop's backend with a products API (GET /products) and a
returns policy in docs/returns.md. No Vatio agent exists yet.
I recommend a WhatsApp and web agent that:
- answers stock and prices by calling GET /products (tool check_stock)
- answers repairs, hours and returns from knowledge written from docs/
- hands complaints and refund requests to a person
Before I write it, I need four answers from you.
#4. Ask the owner only what the code does not answer
Four questions, briefly, all at once:
- What does the business do, and who writes in?
- What must the agent never say or promise?
- When does a person have to take over?
- Which of your systems should it read from: an API, a calendar, a catalog?
Do not invent business facts you were not given: prices, hours, policies, delivery times. If the owner does not know yet, write the instruction so the agent says it does not know and offers a person. Each answer has a place:
| What they tell you | Where it goes |
|---|---|
| What the business does, who writes in | agent.instructions |
| What it must never say or promise | A rules list inside agent.instructions |
| When a person takes over | agent.handoff.when and agent.handoff.after_turns |
| The systems it reads from | One file per action under tools/ |
| Long policies, FAQs, procedures | Knowledge entries |
| The business’s name, description and hours | Not vatio.yml: the inbox’s Business page, or vatio business hours |
#5. Write the agent
#vatio.yml
Create the workspace in the repository the agent belongs in, usually the one of the backend it will call. init writes vatio.yml in the current directory and a Vatio section in AGENTS.md, and needs no account:
vatio init ciclo-norte
Then replace the starter vatio.yml. The example is a bike shop with a repair workshop:
workspace: ciclo-norte
agent:
name: Ciclo Norte
personality: Friendly, direct and brief.
instructions: |
You help customers of Ciclo Norte, a bike shop with a repair workshop.
You answer questions about bikes and parts in stock, repair prices,
opening hours and returns.
Stock and prices: call check_stock. Only say something is in stock, or
quote its price, when check_stock returned it.
Repairs, hours and returns: search knowledge_lookup first. If it does
not answer, say so and offer to pass the question to the team.
Rules you never break:
- Never invent a product, a price or a delivery date.
- Never promise a discount or a repair time.
tools: [check_stock, knowledge_lookup]
knowledge: [shop]
handoff:
after_turns: 10
when:
- a complaint about a purchase or a repair
- the customer asks for a refund
How to write it:
agent.instructionsis the one required field. Write it as what the agent does, then which tool or knowledge answers each kind of question, then the rules. Vatio adds the business context, the contact, the tool descriptions and its own platform instructions, so do not repeat them.- Rules as rules. “Never promise a discount” in its own bullet holds far better than a sentence about tone.
handoffis what gives the agent the tool to hand a conversation to a person, on the conditions inwhen, in the owner’s words.after_turnsis the floor: past that many customer messages it hands off anyway. A customer who asks for a person always gets one.- Language. The agent replies in the visitor’s language. Say so in
instructionsonly if the business needs one language. - An unknown root key fails the push. A
business:orhours:block is ignored with a warning.
#Tools
A tool is one HTTP request to the business’s API. The file name is the tool’s key, and the tool only exists for the agent once its key is in agent.tools. Addresses and keys go in secrets, never in the repository:
vatio secrets set SHOP_API https://api.ciclo-norte.example
vatio secrets set SHOP_API_KEY sk_live_...
tools/check_stock.yml:
description: Check stock and price of a bike, part or accessory.
when_to_use: The customer asks whether something is available or what it costs.
parameters:
type: object
properties:
product: { type: string, description: "Name, model or size, e.g. trail helmet M" }
required: [product]
access: public
request:
method: GET
base_url: "$env.SHOP_API"
path: /products
requires: [params.product]
headers:
Authorization: "Bearer $env.SHOP_API_KEY"
query:
q: "$params.product"
respond:
data:
products: "$.products"
message:
when_empty: No products matched that name.
default: "Found {{count}} matching products."
What breaks tools most often:
- One endpoint per job. Anything that needs branching, a second call or a derived answer is code, and it belongs in the backend. If the endpoint the agent needs does not exist, propose adding it there and say so before writing it.
- Real status codes. A non-2xx response becomes an error the agent can explain; a failure inside a 200 looks like success. An error’s message should say what to change, so the agent can recover on its next call.
- No
localhost. Tools run on Vatio’s servers. The API has to be reachable from the internet; a staging deployment works. - Tests reach the backend. A conversation on preview calls the real API. For writes, give the secret a staging value with
--tests, which every environment but live uses. - Limits. A call times out after 8 seconds, and one reply makes at most 12 tool calls.
- Signed-in customers. A tool that reads a customer’s own data is
access: private, with anauth:block: their backend signs a token saying who the customer is, and Vatio only verifies it. If the app signs in with Supabase, runvatio auth --supabasefirst. Connect your backend covers it.
#Knowledge
Knowledge is Markdown you write, one entry per topic, each heading able to answer a question on its own. A knowledge base listed in agent.knowledge must exist before the push:
vatio kb create shop
vatio kb write shop repairs repairs.md
vatio kb write shop hours-and-returns hours-and-returns.md
vatio kb show shop
Write the entries from the docs and policies in the repository, and from what the owner told you. Nothing on a website reaches the agent unless you put it in an entry.
#6. Check, push and try it
vatio diff
vatio push
diff validates the files and shows what the push would change. The first push signs the developer in: it opens their browser, or prints a link to show them if it cannot. They sign in with email, Google or GitHub and check that the code on the page matches the one in the terminal. If the command gives up waiting, run it again after they have signed in; it picks up the same sign-in page.
There is one preview per workspace. Once the agent is published, a push from a different git branch than the one preview’s unpublished version came from is refused (preview_has_unpublished_push): publish that one first, or replace it with vatio push --force if the developer says so.
Then try the conversations that tell you whether it works:
vatio chat "Do you have a trail helmet in size M?"
vatio chat "The helmet I bought last week broke. I want my money back"
vatio chat --new "Can you give me a discount if I pay cash?"
vatio chat show
| Try | It should |
|---|---|
| The question most customers ask | Call the right tool and answer from what it returned |
| Something neither the tools nor the knowledge cover | Say it does not know and offer a person |
| Something it must never promise | Decline, following the rule |
| A case on the handoff list | Hand off |
--new and a first message | Answer a visitor it has never met |
vatio chat show prints every tool call with what the API returned. When a reply is wrong, find the cause, which is often the tool’s response rather than the instructions, fix it, push again and replay the same conversation.
#7. Hand over the link
vatio push prints a link after Try it:. It opens the Test page in the inbox, where the developer and the owner try the agent:
- Web chat, on the page.
- WhatsApp: a QR code connects their phone to Vatio’s shared preview number, with no Meta account. Up to five test phones.
- Instagram: a shared preview account, up to five test accounts.
Give them the link with what you tried and how it answered, and anything only the owner can decide:
The agent is on preview: <the Try it link>
I tried five conversations:
- stock of a trail helmet: called check_stock, answered with 2 in stock at $45
- broken helmet, wants a refund: handed off to a person
- discount for paying cash: declined, as the rules say
- a question about tyre brands: not in the knowledge, offered a person
- a new visitor saying hello: introduced itself and asked what they needed
Open questions for you: the repair prices in repairs.md are from the
docs folder; please check they are current.
#8. Publish only when asked
vatio publish
Run it when the developer or the owner asks, and not before. The first publish goes straight through; after it, publish wants preview’s version tested with vatio eval and refuses one that breaks a flag live passes. Then the channels:
- Website. A token for their domain, and one script tag:
vatio tokens create --env live --label website --origin https://ciclo-norte.example
<script async src="https://cdn.vatio.ai/v1/widget.js"
data-workspace="ciclo-norte"
data-token="vatpub_REPLACE_ME"></script>
- WhatsApp and Instagram. The owner connects them in the inbox, under Channels, on live: their business number through Meta’s signup, then the Live switch; a professional Instagram account.
vatio whatsappprints the link.
vatio rollback restores the previous live deployment, configuration and knowledge together.
#9. Before you finish
- If an error or the docs left you guessing, say so:
vatio feedback "what you were doing and what was missing". The people who write both read it. - In Claude Code, run
vatio share-session --should-asklast. If it printsyes, show the person you work for what would go (vatio share-session --save FILE) and ask once whether to send it to the Vatio team: send it, not now, or don’t ask again (vatio share-session --never). If a permission check refuses to send it, give themvatio share-session --id ID --yesto run themselves, then check withvatio share-session --list, which marks a sent session. If it printsno, say nothing.
#Mistakes that cost a session
| Mistake | What happens | Instead |
|---|---|---|
vatio init where a vatio.yml exists | The starter agent replaces the real one on push | Work from the existing vatio.yml |
A tool file that is not in agent.tools | The agent never sees the tool | List every key in agent.tools |
base_url on localhost | Every call fails | An API reachable from the internet |
| An API key in a tool’s YAML | The key is in the repository | vatio secrets set, read as $env.KEY |
| A failure returned as a 200 | The agent reads the failure as an answer | Return the real status code |
Business hours or name in vatio.yml | Ignored, with a warning | The inbox’s Business page |
| A knowledge base that does not exist yet | The push fails | vatio kb create first |
| Prices or policies made up to finish the demo | The agent states them to customers | Ask the owner, or have the agent say it does not know |
| Publishing to show progress | Customers get an untested agent | Hand over the preview link and wait |
#When the agent already exists
If the project already has a vatio.yml, the work is different: sign in with the address the developer was invited with, compare the folder with live (vatio diff --env live), explain the agent from vatio.yml, and recommend from what you find. vatio flags lists what supervisors flagged, which the vatio-improve skill works through, and vatio tools lists the tool calls that are failing, which are a fix in the backend they call. Push a change to preview, test it with vatio eval (in Claude Code, in the background: it takes minutes) and publish it with vatio publish. See Improve your agent.
Frequently asked questions
Where is the full Vatio contract for a coding agent?
In the docs, which win wherever a guide disagrees with them. npx @vatio-ai/cli docs prints all of it in the terminal, vatio.ai/docs/docs.md is the same as one Markdown file, and every docs page is also at its own address plus .md.
What if the business has no API?
Start with an agent that answers from knowledge and hands off to a person for anything that needs their systems, and say so to the owner. When an action needs data, the endpoint belongs in their backend, one endpoint per job; propose it and build it only when asked.
Can the agent be tested on WhatsApp before the business connects its number?
Yes. The inbox's Test page connects up to five phones to Vatio's shared preview number with a QR code, and needs no Meta account. Instagram has the same kind of shared preview account. The business connects its own number after publishing.
What should a coding agent never do with a Vatio agent?
Run vatio init where a vatio.yml already exists, since pushing the starter agent it writes replaces the real one; publish without being asked; write API keys into the repository instead of vatio secrets; or invent prices, hours or policies the owner did not give.
Updated: