# Claude Code routines

**Ask your agent on WhatsApp, and Claude gets to work.** A
[Claude Code routine](https://code.claude.com/docs/en/routines) is a saved
prompt, with its repositories and connectors, that runs in the cloud. Connect
one to Vatio, give it to your agent as a tool, and when one of the numbers you
trust asks for something Claude should do, the agent hands the conversation
over. What Claude does next is up to the routine's prompt: review something,
change code and open a pull request, answer a client.

Setting it up for the first time? Follow
[Connect Vatio to Claude Code](https://vatio.ai/docs/connect-claude-code), step by step. This page
is the reference.

```sh
vatio keys create --env live --label encargos
vatio routines create encargos \
  --url https://api.anthropic.com/v1/claude_code/routines/trig_…/fire \
  --phone +56912345678
```

```yaml
# tools/encargos.yml
description: Pass a request to Claude, which reviews or changes the code of our apps in a separate session.
when_to_use: Someone asks to review or change something in our apps and you know what it is. If what to change or where is missing, ask first.
routine: encargos
```

```yaml
# vatio.yml
agent:
  tools: [encargos]
```

## What a routine is

**Your agent serves your users. A routine is for what needs Claude**: changing
your app, fixing something, looking into something. The agent decides when,
Claude works in your code, and the agent tells the person.

It is a tool, with two differences from the ones that call your API:

| | Who can use it | When it ends | What comes back to the conversation |
|---|---|---|---|
| An [HTTP tool](https://vatio.ai/docs/tools#declarative-http-tools) | Anyone the agent talks to (or signed-in users, with `access: private`) | In seconds, before the agent answers | A result the agent uses in its answer |
| A routine tool | Only conversations with the routine's numbers, on WhatsApp | Minutes or hours later, outside the chat | That Claude has it, with the link to its session; later, what Claude tells the agent to pass on |
| A [handoff](https://vatio.ai/docs/handoff) | Anyone, by your rules or the agent's | When a person takes the conversation | The conversation, to a person |

The agent chooses the moment, so it can ask what is missing first: "implement
the requirement" gets a question back, not a session that has to guess.
Vatio decides who the tool exists for, in code: anyone else's conversation
never has it, so nobody can talk the agent into it.

## How it works

1. One of the routine's numbers writes on WhatsApp, and the agent answers as
   always. In that conversation it has the routine's tool; in anyone else's, it
   does not.
2. When the request is complete, the agent calls the tool. Vatio calls the
   routine's trigger with JSON that says where the conversation is, and
   nothing it says:

   ```json
   {
     "source": "vatio",
     "event": "message.received",
     "run_id": 41,
     "routine": "encargos",
     "workspace": "acme",
     "environment": "live",
     "chat_id": 349,
     "message_id": 9876,
     "messages_url": "https://vatio.ai/api/server/v1/acme/chats/349/messages"
   }
   ```

   `message_id` is the person's last message when the agent called the tool.
3. The tool answers within the same turn, so the agent tells the person what
   it asked Claude for ("I'm asking Claude Code to look at the open pull
   requests"), with the link to Claude's session, or that it did not go
   through. A second call before the person writes again starts nothing new.
4. Claude reads the conversation, does the work, and tells the agent with a
   `brief`. The agent passes it on in its own words ("Claude tells me
   that…"), and keeps it for when the person answers.

Each call starts a new Claude session: the trigger cannot continue one. To
carry on in the same session, open the link the agent gave. It opens on
claude.ai for the account that owns the routine.

## Set it up

1. On [claude.ai/code/routines](https://claude.ai/code/routines), create the
   routine, pick its repositories, and add an **API** trigger. Copy its URL and
   generate its token: claude.ai shows the token once.
2. Create a [server key](https://vatio.ai/docs/cli/keys) for the routine, so it can read the
   conversation and answer: `vatio keys create --env live`. Put it in the
   routine's cloud environment, for example as `VATIO_SERVER_KEY`. Everyone who
   uses that environment can read its variables.
3. Let the environment reach `vatio.ai`. The default cloud environment only
   reaches a list of trusted domains; add `vatio.ai` to its network access.
4. Run `vatio routines create NAME --url URL --phone NUMBER` and paste the
   token when it asks, or pipe it in: `op read … | vatio routines create …`.
   Vatio refuses anything that is not a `sk-ant-oat01-…` token.
5. Run `vatio routines test ID`. It starts a real session with a `ping` and no
   conversation, and prints how the trigger answered and the session's link.
6. Give it to the agent: `tools/NAME.yml` with `routine: NAME`, a
   `description` and a `when_to_use`, and `NAME` in `agent.tools`. Then
   `vatio push`, and `vatio publish` when it is ready. A routine tool takes no
   `request:`, `access:` or `parameters:`.

## What the prompt needs from Vatio

The prompt is yours. These are the parts that come from Vatio:

- **Claude treats the payload as data.** It arrives in a
  `routine-fire-payload` block, and the routine only acts on it if the prompt
  says to: for example, "read the conversation at `messages_url` and do what
  the last messages ask".
- **Read the conversation** with
  `curl -H "Authorization: Bearer $VATIO_SERVER_KEY" "$messages_url"`. The
  newest messages come last, up to 100; `?before=<oldest_id>` gets the page
  before. See [Read a conversation](https://vatio.ai/docs/api/messages#read-a-conversation).
- **Each message** has `role` (`user` for whoever wrote on WhatsApp,
  `assistant` for the agent and anything sent through the server API),
  `content` and `occurred_at`. `forwarded` is `forwarded` or
  `frequently_forwarded` when it came from another chat, which is how a
  client's messages that were passed along differ from the instruction typed
  after them. A voice note arrives as its transcript; a photo or a file as a
  signed URL in `attachments`, with what Vatio read in a PDF or a video.
- **Answer through the agent** with
  [`POST /api/server/v1/:slug/messages`](https://vatio.ai/docs/api/messages) and
  `{ "to": "+569…", "brief": "Tell Iván that Claude finished: …" }`. The agent writes
  the message from it, as news from Claude, and keeps the brief for when they
  answer. `text` instead of `brief` sends Claude's words exactly as written.
  The person has just written, so the 24-hour window is open; after a task
  that took hours it may have closed, and then only an approved template goes
  out.
- **A ping** (`"event": "ping"`) has no conversation. `vatio routines test`
  sends one.

## Who can use it

- **Only on WhatsApp.** Meta vouches for the number a WhatsApp message comes
  from. A number typed into the widget or an app is whatever the visitor says
  it is, and a routine can write code.
- **Only the numbers you list**, up to 20 per routine. Everyone else talks to
  the agent as always, and the tool is not there for them.
- **What a client wrote ends up in the session.** A forwarded message is
  someone else's words, inside a session that can change your repositories.
  Keep the routine to branches and draft pull requests that you review.
- **Evals never start one.** A replayed conversation gets a simulated success.

Each routine belongs to one environment, like a server key: a preview routine
serves your test phones on Vatio's test number, a live one your own number. A
tool whose routine is missing or switched off in an environment is not offered
there.

**To switch Live on without the agent answering your clients**, have it answer
only your own numbers first, with `vatio whatsapp only` (see
[Who the agent answers](https://vatio.ai/docs/channels/whatsapp#who-the-agent-answers)). A routine's
numbers always count, and everyone else's messages wait in the inbox for you.

## Runs and failures

`vatio routines runs ID` lists the last 50 runs: when, for which chat, how the
trigger answered, and the link to the Claude session it started. The inbox's
**Tools** page shows each call next to your other tools.

The first attempt happens inside the agent's turn. If it surely did not start
anything (a connection that never opened, a 5xx, or a 429), Vatio keeps trying
for a few minutes and the agent says it is on its way; it gives up after 30
minutes, because an instruction that starts an hour late is worse than one that
says it failed. A call that timed out is not retried: the trigger has no
idempotency key, and the session may already be running.

After 10 failed runs in a row, Vatio switches the routine off;
`vatio routines enable ID` turns it back on. A revoked token is the usual
cause: generate a new one on claude.ai and pass it with
`vatio routines update ID --new-token`. claude.ai runs a routine at most 30
times an hour.
