<!-- https://vatio.ai/pt/guias/conectar-seu-backend · Atualizado: 2026-10-11 -->

> Dê sua API ao seu agente de IA com tools HTTP em YAML, e deixe usuários logados verem os próprios dados com um JWT que seu backend assina e a Vatio verifica.

# Conecte seu agente de IA ao seu backend

Para um agente de IA trabalhar com dados reais, você dá sua API a ele como tools: cada tool é uma requisição HTTP descrita em um arquivo YAML, com os parâmetros que o modelo preenche e os secrets de que a requisição precisa. Com a Vatio você lista essas tools no `vatio.yml`, e o mesmo agente as chama no WhatsApp, no Instagram e na web. Para os dados de um visitante logado, seu backend assina um JWT e a Vatio só verifica, então uma tool privada lê os dados dessa pessoa e de mais ninguém.

Este guia conecta de ponta a ponta a API de uma loja de bicicletas: secrets, uma tool que lê, uma tool que age, o que volta para o agente e tools privadas para clientes logados.

## Por que o agente precisa da sua API

Sem tools, um agente só consegue repetir as instruções e o conhecimento dele. Se você perguntar se tem a Trail 29 no quadro M em estoque, ele ou admite que não sabe ou chuta, e chutar um estoque, um preço ou um agendamento é a coisa mais cara que um agente pode dizer.

Conhecimento é para o que muda devagar: políticas, horários, como um serviço funciona. Tools são para o que é verdade agora (estoque, status de um pedido, horários livres) e para tudo o que faz alguma coisa, como agendar. Seu backend continua sendo a fonte da verdade e continua fazendo as validações; a Vatio opera a conversa e faz a chamada. Cada chamada aparece no inbox, ao lado da resposta que ela produziu.

## Como uma tool funciona

1. Uma tool é um arquivo, `tools/<chave>.yml`; o nome do arquivo é a chave. Ela é ativada quando a chave está em `agent.tools`.
2. O modelo lê `description` e `when_to_use`, decide chamar e preenche `parameters`, um JSON schema.
3. A Vatio monta a requisição, substitui os placeholders e envia dos próprios servidores.
4. `respond` escolhe o que o modelo vê, e o modelo escreve a resposta a partir de `result: ok` ou `result: error`.

Uma tool é uma requisição só, de propósito. Condições, uma segunda chamada ou uma resposta calculada são código: ficam no seu backend, atrás de um endpoint que faz o trabalho inteiro.

## Guarde o endereço e a chave como secrets

```bash
vatio secrets set PEDALIA_API https://api.pedalia.example
vatio secrets set PEDALIA_API_KEY
```

Se você omitir o valor, o `set` pede com a entrada oculta. As tools leem os secrets como `$env.KEY`, as mudanças valem a partir da próxima chamada sem deploy, e `vatio secrets list` mostra nomes, nunca valores.

## Uma tool que lê

`tools/consultar_estoque.yml`:

```yaml
description: Consulta estoque e preço de uma bicicleta, peça ou acessório.
when_to_use: O cliente pergunta se algo está disponível ou quanto custa.
parameters:
  type: object
  properties:
    produto: { type: string, description: "Nome ou modelo do produto" }
  required: [produto]
access: public
request:
  method: GET
  base_url: "$env.PEDALIA_API"
  path: /produtos
  requires: [params.produto]
  headers:
    Authorization: "Bearer $env.PEDALIA_API_KEY"
  query:
    q: "$params.produto"
respond:
  data:
    produtos: "$.data"
  message:
    when_empty: Nenhum produto corresponde a esse nome.
    default: "Encontrei {{count}} produtos."
```

Os valores em `base_url`, `path`, `headers`, `query` e `body` aceitam estes placeholders:

| Placeholder | Origem |
|---|---|
| `$params.<nome>` | Os argumentos da tool, preenchidos pelo modelo |
| `$env.<KEY>` | Os secrets do workspace |
| `$auth.subject`, `$auth.token`, `$auth.claims.<chave>` | O visitante verificado, em tools privadas |
| `$contact.name`, `$contact.email`, `$contact.phone_number` | O perfil do contato |

## Uma tool que age

`tools/agendar_revisao.yml`:

```yaml
description: Agenda um serviço de oficina para a bicicleta do cliente.
when_to_use: O cliente escolheu um serviço e um dia, e confirmou o nome.
parameters:
  type: object
  properties:
    servico: { type: string, description: "ex.: regulagem de freios, revisão completa" }
    dia: { type: string, description: "YYYY-MM-DD" }
    nome: { type: string }
    telefone: { type: string, description: "Só Instagram e web. No WhatsApp a tool usa o número do contato" }
  required: [servico, dia]
access: public
request:
  method: POST
  base_url: "$env.PEDALIA_API"
  path: /agendamentos-oficina
  requires: [params.servico, params.dia]
  headers:
    Authorization: "Bearer $env.PEDALIA_API_KEY"
  body:
    servico: "$params.servico"
    dia: "$params.dia"
    nome: "$params.nome || $contact.name"
    telefone: "$params.telefone || $contact.phone_number"
respond:
  data:
    agendamento: "$.agendamento"
```

Valores ausentes ficam fora da requisição, então liste em `requires` o que o endpoint não pode deixar de receber, e a tool falha antes de enviar meio agendamento. `||` pega o primeiro valor que não está vazio: o que o cliente acabou de dizer, ou o que o perfil do contato já tem. No WhatsApp o perfil já tem o telefone do cliente, então o agente só pede o número no Instagram e na web. Uma tool usada só no WhatsApp pode dispensar o parâmetro: envie `$contact.phone_number` e coloque `contact.phone_number` em `requires`, e, se alguém esconde o número atrás de um nome de usuário do WhatsApp, a Vatio pede o número com um botão de um toque.

## O que volta para o agente

Toda tool devolve `result` (`ok` ou `error`) e uma `message` que não é vazia:

```json
{ "result": "error", "message": "O sábado está lotado. Segunda às 10:00 está livre." }
```

`respond.data` mapeia chaves de saída para caminhos JSON: `$` é o corpo inteiro, `$.data` é o campo `data`. `{{count}}` é o tamanho do primeiro array nos dados mapeados. Sem `respond.message`, a mensagem é o campo `message` da sua própria API, então seu backend pode dizer o que aconteceu com as próprias palavras.

**Devolva status HTTP de verdade.** Um 2xx vira `ok` e qualquer outro, `error`. Um `409` quando o sábado lotou um segundo atrás vira um honesto "segunda às 10:00 está livre", enquanto uma falha escondida dentro de um `200` vira a confirmação confiante de um agendamento que não existe. Uma busca vazia continua sendo `ok`: funcionou, e a resposta foi não. O modelo vê os resultados das tools, então nunca devolva uma credencial em um deles.

## Ative as tools no `vatio.yml`

```yaml
workspace: pedalia
agent:
  name: Pedalia Bicicletas
  instructions: |
    Você ajuda os clientes da Pedalia Bicicletas com estoque, preços,
    agendamentos na oficina e os pedidos deles.

    Estoque e preços: chame consultar_estoque. Nunca diga um preço ou um
    estoque que consultar_estoque não tenha devolvido.

    Oficina: pergunte o serviço e o dia, depois o nome do cliente e,
    exceto no WhatsApp, o telefone. Repita o agendamento antes de chamar
    agendar_revisao. Só diga que está agendado quando agendar_revisao
    tiver devolvido.

    Pedidos: chame meus_pedidos. Se ela não conseguir confirmar quem é o
    cliente, peça que ele entre na conta em pedalia.example.
  tools: [consultar_estoque, agendar_revisao, meus_pedidos, identify_contact]
auth:
  public_key: identity.pub
```

As tools nativas são ativadas do mesmo jeito: `knowledge_lookup`, `identify_contact` (salva um nome que o visitante informa), `request_contact_info` (pede a um visitante do WhatsApp que compartilhe o número dele) e `remember` e `recall` para a memória sobre o contato. `handoff` é anexada quando você declara `agent.handoff`.

## Tools privadas: os dados do próprio visitante

`consultar_estoque` e `agendar_revisao` são **públicas**: rodam para qualquer pessoa. `meus_pedidos` é **privada**: só roda para um visitante cuja identidade seu backend assinou.

```bash
vatio auth --new-key
```

Isso grava dois arquivos, e eles vão para lugares diferentes. **`identity.pub` fica no workspace**, vai no commit e é nomeado pelo bloco `auth:` acima. **`identity.pem` vai para o seu backend** como segredo, por exemplo em uma variável de ambiente `VATIO_IDENTITY_PRIVATE_KEY`, e depois você apaga o arquivo do diretório do workspace. **Nunca faça `vatio secrets set` da chave privada**: quem lê esse armazenamento é a Vatio, e com sua chave privada ali a Vatio poderia emitir identidades em vez de só conferi-las.

Seu backend assina o token com **RS256**:

| Claim | Obrigatório | Valor |
|---|---|---|
| `sub` | sim | O id do seu usuário. Vira `$auth.subject` |
| `aud` | sim | O slug do seu workspace, exato |
| `exp` | sim | Segundos Unix. Mantenha curto: é conferido de novo a cada chamada de tool |
| `name`, `email`, `phone_number` | não | Preenchem o perfil do contato |
| `metadata` | não | Um link `profile` e `badges` que o inbox mostra sobre a pessoa |

Qualquer outra claim chega como `$auth.claims.<nome>`. `aud` é obrigatório porque uma assinatura prova quem assinou, não para quê: sem ele, um token de redefinição de senha assinado com a mesma chave passaria como identidade.

```js
// Node (jsonwebtoken)
import jwt from "jsonwebtoken";

const visitorToken = jwt.sign(
  { sub: String(user.id), name: user.name, email: user.email },
  process.env.VATIO_IDENTITY_PRIVATE_KEY,
  { algorithm: "RS256", audience: "pedalia", expiresIn: "1h" }
);
```

`tools/meus_pedidos.yml` repassa esse token para a sua API:

```yaml
description: Os pedidos recentes do cliente logado e o status de cada um.
when_to_use: Um cliente logado pergunta sobre os próprios pedidos ou entregas.
access: private
request:
  method: GET
  base_url: "$env.PEDALIA_API"
  path: /me/pedidos
  headers:
    Authorization: "Bearer $auth.token"
respond:
  data:
    pedidos: "$.pedidos"
  message:
    when_empty: Este cliente ainda não tem pedidos.
    default: "Encontrei {{count}} pedidos."
```

Seu endpoint verifica o JWT com a chave pública e restringe a consulta ao `sub` dele. Repare no que a tool não recebe: um id de cliente. **Uma tool privada nunca recebe o id do usuário como parâmetro**, porque quem preenche os parâmetros é o modelo, e ele pode ser convencido a preencher esse com o de outra pessoa. A Vatio recusa o deploy:

```text
tool meus_pedidos: a private tool cannot take "user_id" as a parameter — the model
fills parameters and can be talked into filling that one with someone else's id.
Use $auth.subject, which comes from the verified token
```

O mesmo vale para `customer_id`, `account_id`, `member_id`, `patient_id` e `subject`.

### Passe o token para o widget

Coloque o token na tag do widget só para visitantes logados. Quem não está logado não recebe token, e o agente responde com as tools públicas.

```html
<script async src="https://cdn.vatio.ai/v1/widget.js"
        data-workspace="pedalia"
        data-token="vatpub_..."
        data-visitor-token="JWT_ASSINADO"></script>
```

Quando alguém entra na conta sem recarregar a página, chame `window.VatioWidget.identify(novoToken)` (`null` desconecta). A conversa que a pessoa já tinha continua, agora com um nome; um token com outro `sub` começa uma nova, então em um computador compartilhado um cliente nunca vê o chat de outro. Gere um token novo a cada carregamento de página para que uma conversa longa sobreviva ao primeiro `exp`. O SDK de navegador recebe o mesmo token como `visitorToken`, e o SDK de iOS como `VatioChat(vatio, visitorToken:)`.

### Se seus usuários fazem login com Supabase

Pule o par de chaves, a assinatura e o `data-visitor-token`. Rode:

```bash
vatio auth --supabase --sign-in-url https://pedalia.example/login
```

Ou clique em **Conectar Supabase** em **Canais → Supabase**, no inbox, e escolha o projeto; o comando passa a usá-lo. Ele encontra o projeto no seu `.env` (ou no cliente que o Lovable gera), escreve `auth: supabase: https://<ref>.supabase.co` no `vatio.yml` e guarda `SUPABASE_URL` e `SUPABASE_ANON_KEY` como secrets. A partir daí o widget lê a sessão do Supabase que sua página já tem, acompanha login, logout e renovações do token, e no WhatsApp e no Instagram o botão **Entrar** abre sua página de login, onde o widget entrega a sessão. Uma tool privada chama o Supabase como o visitante, então sua **Row Level Security** decide o que ele vê:

```yaml
access: private
request:
  method: GET
  base_url: "$env.SUPABASE_URL"
  path: /rest/v1/orders
  query:
    select: "*"
  headers:
    apikey: "$env.SUPABASE_ANON_KEY"
    Authorization: "Bearer $auth.token"
```

O projeto precisa assinar com JWT signing keys (ES256), não com o secret legado; se não assinar, o push diz como trocar. Veja [Supabase](https://vatio.ai/docs/integrations/supabase) (em inglês).

## Teste

```bash
vatio diff
vatio push
vatio chat "Vocês têm a Trail 29 no quadro M?"
vatio chat "Agenda uma regulagem de freios no sábado, sou a Ana, 5555-0142"
vatio chat --new "Cadê meu pedido?"
vatio chat show
```

Nenhum comando invoca uma tool diretamente: as tools são testadas por conversas, e `chat show` mostra cada chamada, os argumentos e o que seu backend respondeu. A CLI fala como você, o developer, não como um cliente, então teste as tools privadas em uma página sua: crie um token com `vatio tokens create --env preview --origin http://localhost:3000`, entre na conta e leia o chat com `vatio chat show CHAT_ID`. Um token recusado (outra chave, expirado, `aud` errado) deixa o visitante anônimo em vez de falhar, e o `chat show` diz por quê.

Quando já há pessoas reais falando com o agente, a página **Ferramentas** do inbox mostra como seu backend vem respondendo: a taxa de erro de cada tool, as que não responderam a tempo, a latência p50/p95 e as chamadas que falharam com o que seu backend devolveu. Uma tool com falha quase nunca aparece na conversa, porque o agente pede desculpas e segue, então é ali que você fica sabendo.

Seu coding agent lê a mesma página pelo terminal: `vatio tools --env live` a imprime como Markdown que começa dizendo como trabalhá-la, ou como JSON com `--json`, sobre a última hora, o último dia, 7 ou 30 dias (`--period 1h`). Cada chamada que falhou traz o `vatio chat show` que abre a conversa dela, então o agente vai de "esta tool falha 4% das vezes" à requisição, à resposta e à correção no seu backend sem você copiar nada do inbox.

## Erros comuns

- **O WhatsApp e o Instagram são anônimos até o contato entrar na conta.** Não há uma página onde colocar um token, então adicione `sign_in_url` em `auth:` e uma tool privada envia antes ao contato um botão **Entrar** que abre o seu site; veja a [documentação](https://vatio.ai/docs/authentication/sessions#on-whatsapp). Para um cliente que não entrou na conta consultar um pedido, use uma tool pública que receba o número do pedido mais um segundo dado, como o email do pedido, e faça **seu backend verificar se os dois batem**. Ou mande um link para a página logada.
- **As tools não alcançam `localhost`.** Elas rodam nos servidores da Vatio. Exponha seu backend local com um túnel e defina a URL como secret só do preview: `vatio secrets set PEDALIA_API https://pedalia-dev.tunnel.example --env preview`.
- **Os evals chamam suas tools.** Aponte-as para um sandbox com um valor de testes (`vatio secrets set KEY URL --tests`); uma gravação cuja URL não tem um é simulada em vez de enviada, então uma repetição nunca agenda um serviço de verdade.
- **Um endpoint por tarefa.** Se responder exige buscar o cliente, depois o pedido, depois a transportadora, escreva um endpoint que responda à pergunta. Três tools encadeadas deixam a lógica em um prompt.
- **As validações ficam no código.** Se um agendamento pertence a quem está pedindo, quem decide é seu backend, nunca as instruções.

## Próximos passos

Se o agente ainda não existe, comece por [Construir um agente de IA](/pt/guias/construir-um-agente-de-ia). Quando os clientes já usam o agente, os supervisores reportam as respostas erradas e seu coding agent corrige a causa, que muitas vezes é uma resposta de tool sem um campo: veja [Melhorar seu agente](/pt/guias/melhorar-seu-agente-de-ia). A referência completa está em [Tools](https://vatio.ai/docs/tools) e [Authentication](https://vatio.ai/docs/authentication/).

## Perguntas frequentes

### Como conecto um agente de IA à minha própria API?

Descreva cada endpoint como uma tool: um arquivo YAML com uma requisição HTTP, os parâmetros que o modelo preenche e placeholders para seus secrets. Adicione a chave dela em agent.tools no vatio.yml, faça push e teste com vatio chat.

### O agente consegue ler os dados de um usuário logado?

Sim, no seu site ou app, e no WhatsApp e no Instagram depois que ele entra na conta. Marque a tool com access: private e faça seu backend assinar um JWT RS256 para o visitante. A Vatio verifica com sua chave pública e repassa o token para a tool, e seu endpoint restringe a consulta ao sub do token. A Vatio nunca tem a chave privada, então consegue conferir quem a pessoa é, mas nunca afirmar isso por conta própria.

### Por que uma tool privada não pode receber um parâmetro user_id?

Porque quem preenche os parâmetros é o modelo, e ele pode ser convencido a preencher esse com o id de outra pessoa. Uma tool privada que declara user_id, customer_id, account_id, member_id, patient_id ou subject faz o deploy falhar. Leia o usuário de $auth.subject ou do próprio token.

### Tools privadas funcionam no WhatsApp e no Instagram?

Sim, depois que o contato entra na conta: com um sign_in_url no vatio.yml, a Vatio envia um botão Entrar que abre o seu site, sua página redireciona de volta com o mesmo token que você coloca no widget, e a conversa continua. Até lá ele é anônimo e só tools públicas rodam. Para alguém consultar o próprio caso sem entrar na conta, use uma tool pública que receba um número de referência mais um segundo dado, e faça seu backend verificar se os dois batem.

### Meus usuários fazem login com Supabase. Preciso assinar um JWT?

Não. Rode vatio auth --supabase: ele nomeia seu projeto no vatio.yml, e o widget lê a sessão do Supabase que sua página já tem, sem código. No WhatsApp e no Instagram o botão Entrar abre sua página de login e o widget entrega a sessão. Uma tool privada repassa $auth.token para a API REST do Supabase, então sua Row Level Security decide o que o visitante vê.

### O agente consegue chamar uma API rodando em localhost?

Não. As tools rodam nos servidores da Vatio, então localhost fica fora de alcance. Exponha seu backend local com um túnel e defina essa URL como secret só do preview (vatio secrets set KEY URL --env preview), o que muda o valor só ali enquanto o live continua chamando a produção.

### Se uma tool é uma requisição só, onde fica a lógica de vários passos?

No seu backend. Tudo o que precisa de condições, de uma segunda chamada ou de uma resposta calculada é código, então coloque atrás de um endpoint que faz o trabalho inteiro e dê esse endpoint ao agente como tool.
