<!-- https://vatio.ai/guides/coding-agent-playbook · Updated: 2026-10-11 -->

> For Claude Code, Codex, Cursor or any coding agent asked to build an agent on Vatio: what to look at, what to ask, what to write, how to test and what to hand over.

# 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](/guides/build-an-ai-agent) 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:

```bash
npx @vatio-ai/cli docs
```

The same is one Markdown file at [vatio.ai/docs/docs.md](https://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](https://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.yml` whose only root keys are `workspace`, `agent` and `auth`.
- **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 push` deploys to `preview`; `vatio publish` takes it to `live`. 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](https://vatio.ai/docs/api/messages), 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

1. Install the skills.
2. Look before you create.
3. Say what you found, and recommend.
4. Ask the owner only what the code does not answer.
5. Write the agent: `vatio.yml`, tools and knowledge.
6. Check, push and try it.
7. Hand over the link.
8. Publish only when asked.
9. 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:

```bash
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.yml`** at or above the current directory and anywhere in the repository: `git ls-files '*vatio.yml'`. Its `workspace:` line names the workspace. If there is one, the agent exists: never run `vatio init` there, because it writes a starter agent and pushing that replaces the real one. Follow [Joining an existing workspace](https://vatio.ai/docs/#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 doctor` says 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:

```text
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:

1. What does the business do, and who writes in?
2. What must the agent never say or promise?
3. When does a person have to take over?
4. 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:

```bash
vatio init ciclo-norte
```

Then replace the starter `vatio.yml`. The example is a bike shop with a repair workshop:

```yaml
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.instructions` is 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.
- **`handoff`** is what gives the agent the tool to hand a conversation to a person, on the conditions in `when`, in the owner's words. `after_turns` is 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 `instructions` only if the business needs one language.
- **An unknown root key fails the push.** A `business:` or `hours:` 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:

```bash
vatio secrets set SHOP_API https://api.ciclo-norte.example
vatio secrets set SHOP_API_KEY sk_live_...
```

`tools/check_stock.yml`:

```yaml
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 an `auth:` block: their backend signs a token saying who the customer is, and Vatio only verifies it. If the app signs in with Supabase, run `vatio auth --supabase` first. [Connect your backend](/guides/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:

```bash
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

```bash
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:

```bash
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:

```text
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

```bash
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:

```bash
vatio tokens create --env live --label website --origin https://ciclo-norte.example
```

```html
<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 whatsapp` prints the link.

`vatio rollback` restores the previous live deployment, configuration and knowledge together.

## 9. Before you finish

1. 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.
2. In Claude Code, run `vatio share-session --should-ask` last. If it prints `yes`, 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 them `vatio share-session --id ID --yes` to run themselves, then check with `vatio share-session --list`, which marks a sent session. If it prints `no`, 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](/guides/improve-your-ai-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.
