# Documentación de Vatio

Vatio es un runtime para agentes de IA de cara al cliente. Este archivo es el sitio completo (https://docs.vatio.ai/es/) en un solo documento. Cada página también existe por separado en su propia ruta más `.md`.

## Documentación

### Introducción

`https://docs.vatio.ai/es/`

Vatio es un runtime para agentes de IA de cara al cliente.

Defines tu agente en `vatio.yml`, le conectas las herramientas y el conocimiento
que necesita, y lo despliegas a chat web, WhatsApp e Instagram. Vatio corre las
conversaciones, verifica la identidad del visitante y le da a tu equipo una
bandeja para supervisar las respuestas.

#### Empezar

- [Despliega tu primer agente](https://docs.vatio.ai/es/quickstart) — Instala la CLI, escribe seis líneas de YAML y habla con un preview en vivo.
- [Construye con un agente de código](https://docs.vatio.ai/es/cli/mcp) — Apunta Claude Code, Codex o Cursor a Vatio por MCP y deja que despliegue.
- [Referencia de la CLI](https://docs.vatio.ai/es/cli/) — Todos los comandos vatio, qué hacen y qué flags aceptan.

Necesitas **Node.js 20 o superior** y una cuenta de Vatio.

```bash
npm install -g @vatio-ai/cli@latest
vatio init acme
vatio push
```

Los comandos escritos como `vatio` asumen una [instalación global de la
CLI](https://docs.vatio.ai/es/cli/). Siempre puedes reemplazar ese prefijo por
`npx @vatio-ai/cli@latest`.

#### Construir

- [Manifiesto](https://docs.vatio.ai/es/manifest) — El contrato del workspace: un vatio.yml, sus claves raíz y qué gobierna cada una.
- [Herramientas](https://docs.vatio.ai/es/tools) — Dale tu API al agente. Una petición HTTP, descrita en YAML.
- [Conocimiento](https://docs.vatio.ai/es/knowledge) — Entradas que escribes tú y sitios que Vatio lee y relee cada noche.
- [Autenticación](https://docs.vatio.ai/es/authentication/) — Tú firmas un JWT que dice quién es el visitante; Vatio lo verifica y nunca emite uno.

#### Conectar

- [Widget web](https://docs.vatio.ai/es/channels/widget) — Un solo script tag, con el tema del manifiesto o por página.
- [WhatsApp](https://docs.vatio.ai/es/channels/whatsapp) — Prueba en el número compartido en un minuto; conecta el tuyo para tráfico real.
- [Instagram](https://docs.vatio.ai/es/channels/instagram) — Responde DMs con el mismo agente, en una cuenta profesional.
- [SDK de navegador](https://docs.vatio.ai/es/sdk/) — Arma tu propia UI de chat sobre un paquete npm sin dependencias.

#### Para agentes de código

Todo este sitio también es un solo archivo Markdown:
[`/es/docs.md`](https://docs.vatio.ai/es/docs.md), o `vatio docs` desde la
terminal. Cada página existe además por separado en su propia ruta más `.md` —
[`/es/quickstart.md`](https://docs.vatio.ai/es/quickstart.md), por ejemplo — así
un agente puede leer la única página que necesita en vez del contrato completo.
El índice de todas está en [`/es/llms.txt`](https://docs.vatio.ai/es/llms.txt).

```bash
vatio docs --save vatio-docs.md
```

Eso trae la documentación desde el host de Vatio que tengas configurado.
Córrelo de nuevo para refrescar el archivo; la CLI no guarda caché offline.
Para darle a un agente los comandos en vez de la prosa, conecta
[el servidor MCP](https://docs.vatio.ai/es/cli/mcp).

Un prompt inicial:

```text
Read https://docs.vatio.ai/docs.md. Inspect this repository for an existing
vatio.yml before creating a workspace. Build a customer-facing agent
using the product's existing APIs and content. Deploy to preview, test
representative conversations, and show me the preview link and results.
Publish when I ask you to make it live.
```

Para un workspace existente, trabaja desde el directorio que contiene
`vatio.yml` o desde uno de sus subdirectorios, y preserva el slug `workspace`
existente.

Las notas de versión están en el [changelog](https://docs.vatio.ai/changelog).

### Guía rápida

`https://docs.vatio.ai/es/quickstart`

Necesitas **Node.js 20 o superior** y una cuenta de Vatio. Los ejemplos usan
`acme` como slug del workspace; reemplázalo por el tuyo.

#### Instala la CLI

```bash
npm install -g @vatio-ai/cli@latest
vatio --help
```

#### Crea un workspace

Corre estos comandos en el repositorio donde quieres guardar tu agente:

```bash
mkdir support-agent
cd support-agent
vatio init acme
```

`init` crea el workspace remoto y escribe `vatio.yml` en el **directorio
actual**. Abre la autorización de dispositivo si no has iniciado sesión.
Completa ese paso en tu navegador. Si ya eres dueño del workspace remoto,
`init` lo usa.

#### Define el agente

Reemplaza el contenido inicial de `vatio.yml` por:

```yaml
workspace: acme
business:
  name: Acme
  summary: Acme sells warehouse robotics.
agent:
  name: Acme Support
  instructions: |
    Help visitors understand Acme's warehouse robotics.
    Ask what they need help with. If you do not have the information
    to answer, say so. Never invent product details or prices.
  personality: Warm, concise, and clear.
```

`workspace` elige el workspace remoto. `agent.instructions` define qué hace su
único agente. Los demás campos de este ejemplo son opcionales.

#### Despliega y prueba

```bash
vatio push
vatio chat "What does Acme do?"
```

`push` valida tus archivos y despliega a `preview`. Abre el enlace de preview
que imprime, o sigue la conversación con otro comando `chat`.

```bash
vatio chat "Can you tell me the price?"
vatio chat debug
vatio chat reset
```

Verifica que el agente responda desde el contexto que le diste y que reconozca
lo que no sabe. `debug` lee el chat actual de la CLI; `reset` empieza uno nuevo.

#### Publica

Cuando el preview esté listo para clientes:

```bash
vatio publish
```

Tu agente queda disponible en `https://vatio.ai/w/acme`.
`vatio rollback` restaura el despliegue en vivo anterior.

Después, [agrega conocimiento](https://docs.vatio.ai/es/knowledge), [conecta tu API](https://docs.vatio.ai/es/tools) o
[embebe el widget](https://docs.vatio.ai/es/channels/widget).

### Workspace y manifiesto

`https://docs.vatio.ai/es/manifest`

Un workspace contiene un agente y sus conversaciones, contactos, conocimiento,
secretos y conexiones de canal. Su configuración desplegable vive en un
directorio que contiene `vatio.yml`:

```text
support-agent/
  vatio.yml          # requerido
  tools/*.yml        # herramientas
  identity.pub       # clave pública que verifica tu JWT (ver Autenticación)
```

Crea solo los archivos que necesites. La CLI busca el `vatio.yml` más cercano en
el directorio actual o por encima de él, y lee `workspace` para elegir el
remoto. Los comandos de despliegue no tienen flag `--workspace` ni override
`VATIO_WORKSPACE`. Tus credenciales de usuario viven aparte, en
`~/.vatio/config.json`.

Las guías siguientes muestran fragmentos de configuración. Fúndelos en tu
manifiesto existente, dejando una sola copia de cada clave raíz y preservando
las instrucciones y asignaciones de herramientas que sigas necesitando.

#### Claves raíz

Estas son las claves raíz soportadas. Una clave raíz desconocida falla la
validación.

| Clave | Propósito |
|---|---|
| `workspace` | Slug remoto: minúsculas y dígitos separados por guiones |
| `agent` | La definición del agente; requiere `instructions` no vacío |
| `agents` | Varios agentes, con clave por slug, en vez de `agent` |
| `entry` | Qué agente toma una conversación; ver [Varios agentes](https://docs.vatio.ai/es/multi-agent) |
| `business` | Nombre y contexto de la empresa para el agente |
| `widget` | Apariencia web, textos, idioma y orígenes permitidos |
| `auth` | Clave pública que verifica tu JWT; ver [Autenticación](https://docs.vatio.ai/es/authentication/) |

`knowledge` y `links` no son claves raíz: cada agente declara las suyas, dentro
de su bloque `agent:` o `agents:`. `identity` es `auth`. Un manifiesto que
todavía cargue cualquiera de las tres falla el push indicando la migración a
hacer.

Usa `agent: false` solo para un workspace dedicado a la
[API de verificación de teléfono](https://docs.vatio.ai/es/api/phone-verification). No puede además
declarar widget, autenticación, conocimiento, enlaces, herramientas ni helpers
compartidos.

### Agentes

`https://docs.vatio.ai/es/agents`

El bloque `agent` describe un agente:

| Campo | Requerido | Propósito |
|---|---|---|
| `instructions` | Sí | Tareas, reglas de decisión y cuándo usar herramientas |
| `name` | No | Nombre visible; por defecto, la clave del agente |
| `personality` | No | Voz y tono; por defecto, cálido, breve y claro |
| `tools` | No | Claves de herramientas que puede llamar; por defecto, ninguna |
| `knowledge` | No | Bases de conocimiento que este agente lee |
| `links` | No | URLs que este agente puede entregar |

No hay `agent.key` que configurar. Un workspace escrito con `agent:` tiene un
agente, y el runtime lo llama `main`.

```yaml
business:
  name: Acme
  summary: Acme sells warehouse robotics to distributors in Chile and Peru.
agent:
  instructions: |
    Answer product questions using knowledge_lookup.
    If the knowledge does not answer the question, say what is missing.
  tools: [knowledge_lookup]
  knowledge: [docs]
```

`knowledge:` y `links:` pertenecen al agente, no al workspace — cada agente
declara qué lee y qué URLs puede entregar, incluso cuando dos agentes dicen lo
mismo.

Crea la base de conocimiento `docs` con `vatio kb create docs` antes de pushear
este ejemplo: una base referenciada ya debe existir.

#### Contexto del negocio

`business` va en la raíz del manifiesto.
`business.name` acepta hasta 100 caracteres; `business.summary`, hasta 2.000.
Omitir cualquiera de los dos preserva su valor actual en el workspace.

Escribe instrucciones sobre tu producto y tu flujo de trabajo. Vatio agrega el
contexto del negocio, el estado del contacto, las descripciones de herramientas
y sus instrucciones de plataforma. El agente normalmente responde en el idioma
del visitante; especifica un idioma en `instructions` o `personality` si tu
producto lo requiere.

### Varios agentes

`https://docs.vatio.ai/es/multi-agent`

Un workspace puede correr más de un agente, cada uno con sus propias
instrucciones, herramientas y conocimiento. Cuál le toca a un visitante lo
decide el manifiesto, a partir de si tiene sesión iniciada y de lo que diga su
token sobre él.

El caso para el que existe: un sitio de marketing y un portal de clientes que
embeben el mismo widget. El visitante anónimo que pregunta precios y el cliente
con sesión que pregunta por sus propios registros no son la misma conversación,
y un solo set de instrucciones sirviendo a ambos termina no sirviéndole a
ninguno. Antes de esto tenían que ser dos workspaces — dos manifiestos, dos
bases de conocimiento, dos tokens que mantener sincronizados.

Pon los agentes bajo `agents:` y agrega una tabla `entry:`:

```yaml
agents:
  home:
    name: Ana
    instructions: |
      Answer product and pricing questions using knowledge_lookup.
      Nobody here is signed in: never ask for account details.
    tools: [knowledge_lookup]
    knowledge: [public_docs]

  portal:
    name: Ana
    instructions: |
      You are talking to a signed-in customer. Use my_orders and my_documents
      to answer about their own account, and knowledge_lookup for anything else.
    tools: [my_orders, my_documents, knowledge_lookup]
    knowledge: [portal_kb]

  # Cada agente nombra su propio conocimiento; no se hereda nada.

entry:
  - { authenticated: false, agent: home }
  - { claims: { role: staff }, agent: staff }
  - { agent: portal }
```

`agent:` es la forma corta de `agents: { main: … }`, así que un workspace con un
agente no cambia en nada y sigue funcionando exactamente como hoy.

Dale el mismo `name` a todos los agentes cuando la división deba ser invisible.
Para el visitante están hablando con una sola persona todo el tiempo, que es lo
que las reglas de la plataforma ya asumen.

#### La tabla de entrada

Una lista ordenada. Gana la primera fila cuyas condiciones se cumplan, y esa es
toda la regla — no hay especificidad, ni puntaje, ni nada que deducir. La
política se lee de arriba abajo, y alguien que nunca usó Vatio puede revisarla
en un pull request.

| Clave | Significado |
|---|---|
| `agent` | Requerida. El agente que esta fila selecciona |
| `authenticated` | `true` o `false` — si el visitante llegó con un token válido |
| `claims` | Valores de claims que deben coincidir todos; una lista significa cualquiera de ellos |

Una fila sin condiciones matchea a todos. La última fila debe ser una, porque un
visitante que no matchee nada se quedaría sin nadie con quien hablar — y
cualquier cosa escrita después de ella es inalcanzable.

Los claims se comparan como texto, así que `role: 2` en el manifiesto matchea
`"2"` en el token. `vatio tools check` advierte sobre un agente que ninguna fila
puede seleccionar jamás.

#### Cuándo se decide

Antes de la primera respuesta, en todos los canales. Vatio verifica el token al
abrirse la conversación — una verificación de firma, sin red — así que el agente
correcto se elige antes de responder el primer mensaje del visitante, no
después.

Si inicia sesión a mitad de la conversación, el `identify()` del SDK vuelve a
correr la tabla. Alguien que preguntó algo de forma anónima, inició sesión y
volvió conserva todo lo que escribió: mismo hilo, mismo historial y, desde el
siguiente mensaje, el agente que corresponde a quien ahora es.

#### Lo que no es

`entry:` no es un control de seguridad y no debe usarse como tal. Un visitante
sin token válido no tiene identidad verificada, así que ninguna herramienta
`access: private` corre para él, caiga en el agente que caiga. Un error en la
tabla le muestra a alguien el prompt equivocado; no puede mostrarle los datos de
otra persona.

Protege los datos con `access: private` en la herramienta, siempre. La tabla de
entrada es para darle a cada audiencia la conversación correcta.

#### Conocimiento y enlaces por agente

Cada agente declara los suyos, incluso cuando dos digan lo mismo:

```yaml
agents:
  home:
    instructions: …
    knowledge: [public_docs]
    links:
      pricing: "https://acme.test/pricing"
  portal:
    instructions: …
    knowledge: [portal_kb]
    links: {}                   # no entrega ninguna url
```

No hay un valor por defecto a nivel de workspace del cual heredar, y la
repetición es deliberada. Lo que un agente sabe y qué URLs puede entregar son
las dos cosas que revisas cuando responde mal, y un valor heredado significa
mirar en otra parte y después deducir si este agente lo sobrescribió. También
importa en el prompt: cada url declarada aparece listada en él, así que un
agente de portal cargaría toda la lista de enlaces del agente de marketing sin
entregar jamás uno.

### Herramientas

`https://docs.vatio.ai/es/tools`

Las herramientas le permiten al agente llamar a tus APIs. Una herramienta es una
petición HTTP, descrita en YAML. El nombre del archivo es la clave de la
herramienta: `tools/check_stock.yml` se convierte en `check_stock`.

Cualquier cosa que necesite ramificar, una segunda llamada o una respuesta
derivada es código, y pertenece al backend que la herramienta ya llama — dale al
agente un endpoint que haga el trabajo completo.

Agrega cada herramienta a `agent.tools`; crear su archivo no la habilita.

#### Herramientas HTTP declarativas

Define la URL de tu backend como un secreto del workspace:

```bash
vatio secrets set ACME_API https://api.acme.com
```

Crea `tools/check_stock.yml`. Este ejemplo espera que la API devuelva
`{"data": [{"name": "Picker", "stock": 3}]}`:

```yaml
description: Check product stock.
when_to_use: The visitor asks whether a product is available.
parameters:
  type: object
  properties:
    product: { type: string }
  required: [product]
access: public
request:
  method: GET
  base_url: "$env.ACME_API"
  path: /products
  requires: [params.product]
  query:
    name: "$params.product"
respond:
  data:
    products: "$.data"
  message:
    when_empty: No products matched that name.
    default: "Found {{count}} matching products."
```

Fusiona esto en tu bloque `agent` existente:

```yaml
agent:
  instructions: Use check_stock to answer questions about product availability.
  tools: [check_stock]
```

La petición soporta `GET`, `POST`, `PATCH` y `DELETE`. Entrega `method`,
`base_url` y `path`; la URL base debe incluir su esquema y host. `headers` y
`query` son mapas; `body` es un mapa JSON usado para `POST` y `PATCH`.

##### Placeholders

Los valores en `base_url`, `path`, `headers`, `query` y `body` soportan:

| Placeholder | Origen |
|---|---|
| `$params.<name>` | Argumentos de la herramienta |
| `$env.<KEY>` | Secretos del workspace |
| `$auth.subject`, `$auth.token`, `$auth.claims.<key>` | El principal autenticado de la herramienta |
| `$contact.name`, `$contact.email`, `$contact.phone_number` | Perfil del contacto |

Un placeholder solo conserva el tipo de su valor. Un placeholder dentro de un
string se interpola. `||` elige la primera alternativa no vacía, como
`"$params.name || $contact.name"`.

##### Valores requeridos

Los valores faltantes se omiten de la petición. Lista bajo `request.requires`,
sin el prefijo `$`, cualquier valor que el endpoint necesite, para fallar antes
de enviar una petición incompleta. Un teléfono, email o nombre de contacto
faltante produce `missing_contact_phone`, `missing_contact_email` o
`missing_contact_name`; otras rutas producen `missing_requirement`.

##### Mapeo de la respuesta

`respond.data` mapea claves de salida a rutas JSON: `$` es el cuerpo entero,
`$.data` es su campo `data`. `respond.message` puede ser un string literal o el
mapeo `when_empty` / `default` que se muestra arriba. `{{count}}`
es el largo del primer arreglo en los datos mapeados. Sin un arreglo,
`when_empty` aplica cuando todos los valores mapeados están vacíos.

Omite `respond.message` para usar el campo `message` de la propia API. Un HTTP
2xx produce `result: "ok"`; un no-2xx produce `result: "error"`, así que
devuelve el status que describe lo que pasó en vez de codificar fallas dentro de
un 200.

#### Resultados de herramienta

Toda herramienta devuelve un objeto con `result` (`ok` o `error`) y un `message`
no vacío. Se permiten campos adicionales, como `data` y `error_key`.

```json
{ "result": "ok", "message": "This product is out of stock.", "data": { "stock": 0 } }
```

```json
{ "result": "error", "message": "Stock could not be checked.", "error_key": "backend_unavailable" }
```

Usa `ok` cuando la operación se completó, incluyendo un resultado vacío o una
respuesta negativa. Usa `error` cuando no se pudo completar. Un resultado
inválido se convierte en un error de plataforma. Los resultados de herramienta
son visibles para el modelo; nunca devuelvas credenciales.

Valida con `vatio tools check`, despliega con `vatio push` y ejercita la
herramienta a través de `vatio chat`. No hay un comando para invocar una
herramienta directamente.

#### Herramientas incorporadas

| Herramienta | Propósito |
|---|---|
| `knowledge_lookup` | Buscar en las bases listadas en `knowledge` |
| `identify_contact` | Guardar un nombre que el visitante entregó |
| `request_contact_info` | Pedirle a un visitante de WhatsApp que comparta su teléfono |
| `handoff` | Entregar la conversación a una persona — ver [Humano en el loop](https://docs.vatio.ai/es/handoff) |

Habilita las herramientas incorporadas a través de `agent.tools`, igual que las
tuyas. La única excepción es `handoff`, que no tiene interruptor propio:
declarar `agent.handoff` es lo que la adjunta, así que un agente nunca puede
poder escalar sin tener a dónde escalar.
`request_contact_info` envía una solicitud para compartir; el número llega solo
si el visitante lo comparte en un mensaje posterior. Termina el turno y
reintenta la operación dependiente después de esa respuesta.

Una herramienta que lee los registros propios de un visitante con sesión
necesita [`access: private`](https://docs.vatio.ai/es/authentication/tools) y un bloque `auth:`.

### Bases de conocimiento

`https://docs.vatio.ai/es/knowledge`

Una base de conocimiento guarda **entradas**, y una entrada es Markdown. O la
escribes tú, o la escribe un **sitio**: un sitio es una URL — o un patrón de
URL — que Vatio lee, convierte a Markdown y vuelve a leer cada noche.

```bash
vatio kb create docs
vatio kb follow docs acme.com/help/**      # una entrada por página que matchee
vatio kb write docs horarios horarios.md   # o escribe una tú
vatio kb show docs
```

Crea una base por cada conjunto de páginas que quieras mantener separadas —
cualquier cosa que solo un agente deba leer va en su propia base.

Referencia la base en el agente que la lee, y dale `knowledge_lookup`:

```yaml
agent:
  instructions: |
    Search knowledge_lookup before answering questions about Acme's policies.
    If the results do not answer the question, say so.
  tools: [knowledge_lookup]
  knowledge: [docs]
```

Corre `vatio push` después de cambiar estas referencias. Una base referenciada
ya debe existir — un nombre mal escrito falla el push en vez de convertirse
silenciosamente en una base vacía. Quitar una referencia no borra su contenido.

#### Sitios

Un sitio es un campo y una regla: una URL es esa URL, un patrón es toda página
que lo matchee.

| Lo que escribes | Lo que lee |
| --- | --- |
| `acme.com` | la home, y solo ella |
| `acme.com/help` | esa página |
| `acme.com/**` | todas las páginas del sitio |
| `acme.com/help/**` | esa sección, a cualquier profundidad |
| `acme.com/blog/*` | los posts directamente bajo `/blog` |

`*` matchea dentro de un segmento de ruta; `**` matchea a través de segmentos.
Nada está implícito: `acme.com/help` nunca significa "y todo lo que hay debajo".
Las URLs deben ser HTTP o HTTPS públicas.

Vatio descubre páginas por una ruta exacta, `llms.txt`, un sitemap o los enlaces
del sitio. Respeta `robots.txt` y puede renderizar páginas cuyo contenido
necesita JavaScript. El contenido detrás de un clic o una búsqueda conviene
escribirlo como entrada.

Todos los sitios se releen cada noche, y `vatio kb refresh BASE [URL]` los lee
ahora. Una página cuyo Markdown no cambió cuesta una petición y nada más — no se
reindexa ni se vuelve a embeber.

`vatio kb unfollow BASE URL` deja de leer un sitio y borra las entradas que
escribió: son una copia de páginas que viven en otra parte, y conservarlas
dejaría a la base respondiendo desde algo que ya nada refresca.

#### Entradas

```bash
vatio kb write docs refunds refunds.md   # la crea, o reemplaza lo que haya
vatio kb cat docs refunds                # exactamente lo que Vatio guarda
vatio kb cat docs refunds | edit | vatio kb write docs refunds
```

`vatio kb write BASE ENTRY [FILE]` pone Markdown bajo un nombre, leyendo stdin
cuando no se entrega archivo. El título de la entrada sale de su propio
`# heading`. Los headings dividen una entrada en secciones buscables; escribe
cada una para que pueda responder una pregunta por sí sola.

Una entrada que escribió un sitio **no es editable** — la próxima lectura la
sobrescribiría sin avisar. Cambia el sitio, o escribe tu propia entrada.

Usa `vatio kb show docs` para revisar el estado y los errores de cada sitio, y
qué entradas están listas para responder. La lectura y la indexación son
asíncronas: una entrada aparece lista una vez que sus secciones se embebieron.
Prueba la recuperación en una conversación después.

`vatio kb rm-entry BASE ENTRY` borra una entrada. `vatio kb rm BASE` borra una
base solo cuando ningún agente desplegado la referencia.

**El conocimiento es compartido por todos los entornos.** Escribir, refrescar y
borrar contenido puede cambiar respuestas en vivo de inmediato. El rollback de
despliegue no restaura contenido de conocimiento.

#### Permitir a VatioBot

Si eres dueño de un sitio cuyo `robots.txt` bloquea el crawling, permite a
VatioBot en el `robots.txt` de ese sitio:

```text
User-agent: VatioBot
Allow: /
```

Después corre `vatio kb reindex BASE SOURCE`. No hay opción para saltarse la
política de crawling del sitio.

### Enlaces

`https://docs.vatio.ai/es/links`

Declara destinos estáticos en el agente que puede entregarlos:

```yaml
agent:
  instructions: …
  links:
    help: https://acme.com/help
    product:
      url: https://acme.com/products/{category}
      when: The visitor wants to browse a product category.
      values:
        category: [picking, packing, sorting]
```

Un valor puede ser un string con la URL o un mapeo con `url`, un `when`
opcional y `values` para cada placeholder. Las URLs deben ser HTTP o HTTPS
absolutas. Los valores de placeholder usan letras, dígitos, `.`, `_`, `~` o `-`.

Vatio expande todas las combinaciones al desplegar, con un límite de 100 URLs.
Usa una herramienta para destinos dinámicos o catálogos más grandes. Los enlaces
que devuelve una herramienta quedan permitidos por el resto de esa conversación.

Una URL escrita directamente en `instructions` también es compartible, así que
una mención puntual no necesita entrada acá. `links` sigue siendo el mejor lugar
para un destino que el agente debería efectivamente ofrecer: lleva un `when` que
le dice cuándo recurrir a él, y expande placeholders.

Qué URLs puede llevar una respuesta lo impone el
[sistema de resguardos](https://docs.vatio.ai/es/safeguards).

### Humano en el loop

`https://docs.vatio.ai/es/handoff`

Dos bloques del agente deciden cuándo toma una persona.

```yaml
agent:
  instructions: ...
  hours:
    timezone: America/Santiago
    mon: 09:00-18:00
    tue: 09:00-13:00, 14:00-18:00
    sat: 10:00-14:00
  handoff:
    after_turns: 8
    when:
      - el cliente pide hablar con una persona
      - reclamo por un cobro
```

Declarar `handoff` es lo que le da al agente la herramienta para entregar una
conversación. Llamarla marca el chat como esperando a una persona; aparece así
en [la bandeja](https://docs.vatio.ai/es/channels/inbox), y quien responda ahí o desde la app de
WhatsApp Business se la lleva. Vatio todavía no le avisa a nadie.

El agente lo dice de frente. Es un asistente, no pretende otra cosa, y cuando
entrega algo le dice al visitante que está consultando con un colega y que va a
volver con una respuesta — sin inventar un trámite para cubrirlo. Varias
jurisdicciones exigen revelar que se está hablando con una máquina, y un solo
comportamiento honesto es una cosa menos que hacer mal.

#### Cuándo escala

**`handoff.when`** es tu propia lista de condiciones, en tus palabras, y va al
prompt. **`handoff.after_turns`** es el piso determinístico bajo ella: pasados
esos mensajes del visitante el handoff ocurre haya o no pedido el modelo uno.
Un visitante que pide un humano con todas sus letras siempre dispara uno — eso
nunca depende del criterio del modelo. No declares ninguno de los dos y escalar
queda enteramente al criterio del modelo.

#### Horarios

**`hours`** dice cuándo hay una persona de turno, en una zona horaria real,
rangos de 24h, una línea por día. Un día que omites es un día en que no hay
nadie. Los horarios **no** deciden quién responde — el agente tría e intenta
resolver igual. Deciden qué pasa después de que se rinde:

| | Dentro de horario | Fuera de horario |
|---|---|---|
| El agente | Se calla, hay alguien para tomarla | Sigue respondiendo — prometió una persona para la mañana, y quedarse callado encima es peor que ayudar |

No declares `hours` y el agente se trata como siempre alcanzable, así que un
handoff siempre lo calla. Un handoff que nadie toma en una hora devuelve la
conversación al agente, en vez de dejar al visitante con una promesa y silencio.

### Resguardos

`https://docs.vatio.ai/es/safeguards`

Vatio revisa los borradores de respuesta buscando contenido vacío, medios
generados y enlaces no autorizados. Una revisión fallida dispara un intento de
corrección. Estas revisiones corren para todo agente y no se pueden desactivar
desde el manifiesto.

Las respuestas son texto. El agente puede leer medios entrantes soportados, pero
no puede generar ni enviar archivos. Las respuestas web usan Markdown; WhatsApp
e Instagram usan texto plano.

Una respuesta solo puede llevar una URL que al agente ya le fue entregada: una
declarada en [`links`](https://docs.vatio.ai/es/links), una escrita en cualquier otro lugar que
llegue al prompt (`instructions`, `personality`, la descripción del negocio, el
`when_to_use` de una herramienta), o una que una herramienta devolvió en la
conversación. Debe estar copiada exactamente; una URL que el agente armó o
alteró se elimina. Nada que el visitante haya escrito queda permitido.

### Autenticación

`https://docs.vatio.ai/es/authentication/`

**Tú firmas un JWT que dice quién es el visitante. Vatio lo verifica y se lo
pasa a tus herramientas.** Eso es toda la autenticación — hay un solo
mecanismo, en todos los canales.

Vatio guarda solo tu clave **pública**, así que puede revisar un token pero
nunca emitir uno. Tu backend sigue siendo lo único que puede decir quién es
alguien.

#### Configúralo

Genera el par de claves:

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

Eso escribe dos archivos, y van a lugares distintos.

**`identity.pub` se queda en el workspace.** Es una clave pública, así que se
commitea como cualquier otro archivo y `vatio.yml` la nombra:

```yaml
### vatio.yml
auth:
  public_key: identity.pub
```

**`identity.pem` va a tu propio backend**, como secreto — una variable de
entorno, o el almacén de credenciales que ya uses. Tu backend es lo que firma,
así que es lo único que alguna vez la necesita. Cárgala y después borra el
archivo del directorio del workspace; ahí no tiene ninguna función.

```bash
### variable de entorno
VATIO_IDENTITY_PRIVATE_KEY="$(cat identity.pem)"

### o credenciales de Rails
bin/rails credentials:edit     # vatio: { identity_private_key: "..." }
```

**Nunca hagas `vatio secrets set` de la clave privada.** Ese almacén lo lee
Vatio, para que tus herramientas puedan llamar a tu API. Una clave privada ahí
le permitiría a Vatio *emitir* tokens por tus usuarios en vez de solo
revisarlos, que es precisamente lo único que este diseño existe para prevenir.
Vatio guarda la mitad pública y nada más.

Marca cada herramienta que necesita un usuario con sesión:

```yaml
### tools/my_bookings.yml
access: private
```

Las herramientas sin `access: private` son públicas y corren para cualquiera.

#### El token

Fírmalo con `identity.pem` usando **RS256**:

| Claim | Requerido | Valor |
|---|---|---|
| `sub` | sí | El id de tu usuario. Se convierte en `$auth.subject`. |
| `aud` | sí | El slug de tu workspace, exacto. |
| `exp` | sí | Segundos Unix. Mantenlo corto — Vatio lo revisa en cada llamada a herramienta. |
| `name` | no | Llena el nombre del contacto. |
| `email` | no | Llena el email del contacto. |
| `phone_number` | no | Llena el teléfono del contacto. |

Cualquier otro claim que agregues llega como `$auth.claims.<name>`.

```ruby
### Ruby
JWT.encode(
  { sub: user.id.to_s, aud: "acme", exp: 1.hour.from_now.to_i,
    name: user.name, email: user.email },
  OpenSSL::PKey::RSA.new(ENV["VATIO_IDENTITY_PRIVATE_KEY"]), "RS256"
)
```

```js
// Node (jsonwebtoken)
jwt.sign(
  { sub: String(user.id), name: user.name, email: user.email },
  process.env.VATIO_IDENTITY_PRIVATE_KEY,
  { algorithm: "RS256", audience: "acme", expiresIn: "1h" }
);
```

```python
### Python (PyJWT)
jwt.encode(
    {"sub": str(user.id), "aud": "acme",
     "exp": int(time.time()) + 3600, "name": user.name},
    os.environ["VATIO_IDENTITY_PRIVATE_KEY"], algorithm="RS256",
)
```

`aud` es requerido porque una firma prueba *quién* firmó, no *para qué*. Sin él,
cualquier otro JWT que firmes con la misma clave — un reseteo de contraseña, un
enlace de descarga — sería aceptado acá como identidad.

Sigue: cómo llega el token a Vatio en
[cada canal](https://docs.vatio.ai/es/authentication/sessions), y qué ve una
[herramienta privada](https://docs.vatio.ai/es/authentication/tools) cuando llega.

### Sesiones y canales

`https://docs.vatio.ai/es/authentication/sessions`

#### En la web

Renderiza el token en el script tag del widget. Los visitantes sin sesión
simplemente no reciben token, y el agente responde con sus herramientas
públicas.

```erb
<script src="https://cdn.vatio.ai/v1/widget.js"
        data-workspace="acme"
        data-token="vatpub_..."
        <%= "data-visitor-token=\"#{visitor_jwt}\"".html_safe if current_user %>></script>
```

Para iniciar sesión sin recargar la página:

```js
window.VatioWidget.identify(newToken);   // null para cerrar sesión
```

#### Iniciar sesión a mitad de conversación

Un visitante pregunta algo, el agente le dice que inicie sesión, lo hace y
vuelve. **La conversación sobrevive.** Ya sea que haya iniciado sesión con
`identify()` o navegando y recargando la página con un token, el chat que ya
tenía se conserva y ahora tiene un nombre — así el agente puede responder la
pregunta que motivó el login sin que la tenga que reescribir.

| Antes | Después | Qué pasa |
|---|---|---|
| anónimo | con sesión | Misma conversación, ahora identificada |
| usuario A | usuario A, token más nuevo | Misma conversación, token refrescado |
| usuario A | usuario B | **Conversación nueva** |
| con sesión | sin sesión | Conversación nueva |

Los dos últimos son el caso del notebook compartido: una conversación que
pertenecía a un `sub` nunca se le entrega a otro, así que iniciar sesión como
otra persona se ve exactamente como llegar por primera vez. Vatio lo impone del
lado del servidor — la página no puede optar por salirse.

Refrescar es también cómo se mantiene viva una sesión web: renderiza un token
fresco en cada carga de página y una conversación larga sigue funcionando más
allá del `exp` del primer token.

#### Canales sin sesión

En WhatsApp e Instagram no hay página donde renderizar un token, así que Vatio
le pide uno a tu backend. Agrega `mint:`:

```yaml
auth:
  public_key: identity.pub
  mint:
    url: $env.API_URL/api/vatio/identity
    headers:
      X-Api-Key: $env.API_KEY
```

Vatio envía la evidencia del canal y espera un token de vuelta:

```http
POST /api/vatio/identity
Content-Type: application/json

{ "channel": "whatsapp", "phone_number": "+56912345678" }
```

```json
{ "token": "<el mismo tipo de JWT que firmas para la web>" }
```

Devuelve cualquier no-2xx para un número que no reconozcas — el visitante queda
anónimo y el agente conserva sus herramientas públicas. Define los secretos que
necesite con `vatio secrets set API_KEY ...`.

Mint corre una vez por conversación, y otra vez solo si el token expira. En
WhatsApp eso significa que una conversación larga se refresca sola.

**Mint nunca se usa en la web**, aunque lo declares. En la web el token es la
evidencia: la página tenía una sesión y lo dijo con una firma. Un visitante web
sin token válido está sin sesión, y sigue así hasta que la página llame a
`identify()` con uno fresco.

### Herramientas privadas

`https://docs.vatio.ai/es/authentication/tools`

#### Qué ve una herramienta

| Placeholder | Valor |
|---|---|
| `$auth.subject` | El `sub` del token |
| `$auth.token` | El JWT crudo, para reenviarlo a tu API |
| `$auth.claims.<name>` | Cualquier claim propio |

```yaml
### tools/my_bookings.yml
description: The visitor's upcoming bookings.
when_to_use: When they ask about their own bookings.
access: private
request:
  method: GET
  base_url: $env.API_URL
  path: /api/bookings
  headers:
    Authorization: Bearer $auth.token
respond:
  data:
    bookings: bookings
```

Tu endpoint verifica el mismo JWT con la misma clave pública, y acota la
consulta a su `sub`.

#### Reglas

**Nunca tomes el id del usuario como parámetro.** El modelo llena los
parámetros, y se le puede convencer de llenar ese con el id de otra persona. Una
herramienta privada que declare `user_id`, `customer_id`, `account_id`,
`member_id`, `patient_id` o `subject` falla el despliegue:

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

Deriva el usuario desde el token. Un endpoint que acepta un id tiene que
acordarse de acotarlo cada vez, para siempre; uno que lee el token no se puede
olvidar.

Otros errores que puedes encontrar:

| Error | Solución |
|---|---|
| `access: private needs an auth: block` | Agrega `auth:` con `public_key:` |
| `auth.public_key ... is a PRIVATE key` | Apunta a `identity.pub`, no a `identity.pem` |
| `auth.algorithm must be one of RS256...` | Firma con RS256/ES256, no HS256 — un secreto compartido no puede vivir en un manifiesto |
| `auth.mint.url must be https://` | Usa `https://` o un placeholder `$env.` |
| `access must be "public" or "private"` | Los nombres de esquema ya no existen; usa `private` |
| `auth/ holds JavaScript auth providers, which are gone` | Borra `auth/` y firma un JWT |
| `JavaScript tools are gone` | Borra `tools/*.js` y `lib/*.js`; describe la llamada en `tools/<key>.yml` |

Un token rechazado — clave equivocada, expirado, `aud` equivocado — deja al
visitante anónimo en vez de dar error, y el agente cae de vuelta a sus
herramientas públicas. `vatio chat` muestra la razón.

### Canales

`https://docs.vatio.ai/es/channels/`

El mismo agente atiende chat web, WhatsApp, Instagram y conversaciones de CLI.
Usa el canal real cuando pruebes identidad de canal o entrega de mensajes.

- [Widget web](https://docs.vatio.ai/es/channels/widget) — Un script tag en tu propio sitio, o la página que Vatio publica.
- [WhatsApp](https://docs.vatio.ai/es/channels/whatsapp) — Un número de prueba compartido, y después el tuyo para tráfico real.
- [Instagram](https://docs.vatio.ai/es/channels/instagram) — DMs en una cuenta profesional, con el mismo agente detrás.
- [Bandeja](https://docs.vatio.ai/es/channels/inbox) — Donde tu equipo mira las conversaciones y toma una.

#### Comportamiento por canal

WhatsApp e Instagram continúan la última conversación hasta que el último
mensaje del visitante tenga más de ocho horas. El siguiente mensaje empieza un
chat nuevo.

Las respuestas son solo texto. Las imágenes, audios y PDFs entrantes soportados
pueden pasarse al modelo; los archivos soportados deben caber en el límite de
8 MB por archivo. Los adjuntos no soportados o sobredimensionados quedan
disponibles para los supervisores, y el agente recibe un aviso de que no puede
leerlos.

El chat web recibe una respuesta completa, que el widget renderiza
progresivamente. WhatsApp e Instagram reciben mensajes pausados con indicadores
de escritura. Las respuestas de CLI son inmediatas cuando están listas. Son
estilos de entrega, no agentes distintos.

### Widget web

`https://docs.vatio.ai/es/channels/widget`

La página alojada en `https://vatio.ai/w/acme` funciona después de publicar.
Para embeber el chat en tu propio sitio web, agrega su origen a `vatio.yml`:

```yaml
widget:
  allowed_origins:
    - https://acme.com
    - http://localhost:3000
  accent_color: "#3355FF"
  about: Ask Acme Support about products and orders.
  greeting: How can I help?
  suggestions:
    - What does Acme sell?
    - Where is my order?
```

Los orígenes son valores exactos `scheme://host[:port]`, sin ruta ni comodín.
Una lista vacía bloquea los embeds externos; las páginas alojadas por Vatio
siguen funcionando. Pushea el manifiesto, publica el agente si hace falta, y
crea un token:

```bash
vatio push
vatio publish
vatio tokens create --env live --label website
```

Copia el token `vatpub_` que devuelve en tu página:

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

Los tokens publicables están pensados para el código fuente de la página y
eligen un workspace y un entorno. No autorizan desplegar ni acceder a la bandeja
del workspace. Nunca uses un token de desarrollador `vat_` en un navegador.

`vatio widget` reporta la configuración del servidor y los prefijos de tokens
existentes. `vatio tokens list` lista los tokens; `vatio tokens revoke PREFIX`
revoca uno. La revocación impide nuevos chats y listados de conversaciones con
ese token; las credenciales de chat existentes siguen válidas hasta expirar.

#### Configuración del widget

Los valores visuales se resuelven en este orden: override de la página,
configuración del workspace, default de la plataforma. Usa `widget` en
`vatio.yml` para los defaults compartidos y atributos `data-*` para una página
específica.

| Clave del manifiesto | Atributo de página | Valor / default |
|---|---|---|
| `accent_color` | `data-accent` | Color de marca `#RRGGBB` |
| `accent_ink` | `data-accent-ink` | `#RRGGBB`; si no, contraste automático |
| `surface`, `ink`, `muted`, `line` | `data-surface`, `data-ink`, `data-muted`, `data-line` | Colores de tema `#RRGGBB` |
| `scheme` | `data-scheme` | `auto`, `light` o `dark`; default `auto` |
| `position` | `data-position` | `right` o `left`; default `right` |
| `font` | `data-font` | Stack de fuentes CSS; `inherit` usa la del sitio |
| `radius` | `data-radius` | Largo CSS como `16px`; default `16px` |
| `title` | `data-title` | Hasta 200 caracteres; default, el nombre del agente |
| `greeting` | `data-greeting` | Hasta 200 caracteres; default, texto localizado |
| `suggestions` | `data-suggestions` | Hasta cuatro prompts de 200 caracteres; el atributo usa texto separado por pipes |
| `about` | `data-about` | Hasta 2.000 caracteres de texto visible al visitante |
| `locale` | `data-locale` | `en`, `es` o `pt`; default `en` |
| `logo` | — | PNG, JPEG, WebP o GIF relativo al workspace, hasta 2 MB |
| `allowed_origins` | — | Orígenes autorizados a usar la API pública |

`about` se le muestra a los visitantes; `business.summary` entrega contexto al
modelo. `locale` define las etiquetas de interfaz; el idioma del agente sigue a
la conversación. Una clave de widget desconocida falla la validación.

Por ejemplo, `data-suggestions="Products|Track an order"` define dos prompts.

#### Chat a página completa

Para un chat a página completa, usa un contenedor y atributos específicos de la
página:

```html
<div id="chat" style="height:100dvh"></div>
<script async src="https://cdn.vatio.ai/v1/widget.js"
        data-workspace="acme" data-token="vatpub_REPLACE_ME"
        data-display="page" data-mount="#chat"></script>
```

`data-display` es `bubble` por defecto. `data-mount` elige el contenedor en modo
página y por defecto es el body. Estos atributos no van en el manifiesto. El
widget normalmente espera la carga de la página y un momento de inactividad;
`data-eager="true"` lo arranca de inmediato.

Para decirle al agente quién tiene la sesión iniciada, renderiza un token de
visitante en el mismo tag — ver [Sesiones y
canales](https://docs.vatio.ai/es/authentication/sessions). Para armar tu propia UI en vez de embeber
esta, usa el [SDK de navegador](https://docs.vatio.ai/es/sdk/).

### WhatsApp

`https://docs.vatio.ai/es/channels/whatsapp`

#### Prueba con el preview compartido

No necesitas tu propia cuenta de Meta:

```bash
vatio whatsapp numbers add +56912345678
vatio whatsapp numbers verify +56912345678 123456
```

Reemplaza el número por tu teléfono y `123456` por el código que recibas.
Después escríbele al número compartido desde ese teléfono. Siempre llega a
`preview`, incluso cuando tienes previews con nombre o un despliegue en vivo.

Puedes registrar hasta cinco teléfonos de prueba por workspace. Los códigos
duran 10 minutos; los reenvíos tienen 60 segundos de espera. Administra los
registros con `vatio whatsapp numbers list`, `resend PHONE` y `remove PHONE`.
Un teléfono verificado puede rutear a un solo workspace a la vez; agregar uno ya
verificado en otro workspace tuyo lo mueve a este.

#### Conecta tu número de negocio

Para tráfico real:

```bash
vatio whatsapp connect
```

Completa el registro de Meta en el navegador. Después inspecciona la conexión y
activa las respuestas tras publicar tu agente:

```bash
vatio whatsapp check
vatio whatsapp activate
```

Un número recién conectado queda en pausa. El estado reporta credenciales,
recepción de webhook y activación por separado. `vatio whatsapp deactivate`
pausa las respuestas; `disconnect` elimina la conexión. Reconectar el mismo
número renueva sus credenciales. Desconecta primero para cambiar de número.

El flujo de conexión permite conservar tu app de WhatsApp Business en el
teléfono. Una respuesta enviada desde esa app inicia el traspaso a humano en la
conversación; libera el chat en [la bandeja](https://docs.vatio.ai/es/channels/inbox) cuando el
agente deba retomar.

Identificar a un visitante de WhatsApp implica emitir un token desde tu propio
backend — ver [Canales sin
sesión](https://docs.vatio.ai/es/authentication/sessions#canales-sin-sesion).

### Instagram

`https://docs.vatio.ai/es/channels/instagram`

#### Prueba con el preview compartido

```bash
vatio instagram accounts add @yourhandle
```

Mándale un DM a la cuenta compartida que imprime el comando **desde ese
handle**. Vatio envía un código por DM. Confirma el código recibido:

```bash
vatio instagram accounts verify 123456
```

Las cuentas de prueba verificadas siempre llegan a `preview`. Se permiten hasta
cinco por workspace. `vatio instagram accounts list` muestra el ID y el estado
de cada cuenta: `declared`, `awaiting_code` o `verified`. Usa `resend ID` o
`remove ID` para administrar un registro. La CLI no espera el primer DM.

#### Conecta tu cuenta profesional

Para DMs reales:

```bash
vatio instagram connect
```

Completa el consentimiento de Meta en el navegador, y después corre
`vatio instagram check`. Un workspace conecta una cuenta; una cuenta se conecta
a un workspace. Vatio refresca su access token automáticamente. Si expira,
reconecta la misma cuenta. Usa `vatio instagram disconnect` antes de cambiar de
cuenta.

### Bandeja

`https://docs.vatio.ai/es/channels/inbox`

Invita supervisores desde **Team** en la consola de Vatio. Inician sesión con un
código enviado a su email y llegan a `/workspaces/<id>/inbox` — todas las
conversaciones, las respuestas, el traspaso a humano, las calificaciones y las
llamadas a herramientas del agente. Ven eso y nada más: ni despliegue, ni
herramientas, ni conocimiento. Quítalos desde la misma página y la siguiente
petición que hagan queda rechazada, incluido un socket que ya tuvieran abierto.

No hay nada que embeber ni token que emitir. Tener sesión en Vatio, como alguien
a quien este workspace invitó, es todo el modelo de acceso.

Responder inicia el **traspaso a humano**, que pausa las respuestas del agente
en ese chat — desde la bandeja, o desde la app de WhatsApp Business en tu propio
número, que le llega a Vatio como eco y cuenta igual. Devuélvela desde la
bandeja cuando termines, o déjala: un traspaso expira **8 horas después de lo
último que dijo la persona**, y el agente retoma la conversación la próxima vez
que escriba el visitante. El reloj se reinicia con cada respuesta humana, así
que una conversación que alguien está sosteniendo activamente nunca expira por
debajo.

Para que el agente pida una persona por sí mismo, en vez de esperar a que
alguien note, declárale [`handoff` y `hours`](https://docs.vatio.ai/es/handoff).

### Despliegues

`https://docs.vatio.ai/es/deployments`

#### Preview y live

Un despliegue es una instantánea guardada de la configuración. Un entorno es un
nombre que apunta a un despliegue. `preview` es el objetivo de desarrollo por
defecto; `live` es el objetivo de cara al cliente.

```bash
vatio push
vatio status
vatio diff --env live
vatio publish
vatio rollback
```

`push` valida y actualiza un preview; no puede apuntar a `live`. `publish`
promueve un preview. `rollback` restaura la configuración live anterior.
`diff --env live` compara tu **directorio local** con live, así que pushea las
ediciones locales antes de publicarlas.

Los agentes, herramientas, configuración de auth, chats y contactos tienen
alcance por entorno. **Los secretos, el contenido de conocimiento, las
conexiones de canal, los campos de negocio y la configuración del widget son de
todo el workspace.** Aplicar un manifiesto puede actualizar la configuración de
negocio y de widget durante un push a preview. Una herramienta en preview
también puede llamar al mismo backend externo que live. Usa un workspace aparte
cuando estos recursos deban estar aislados.

El rollback restaura la configuración del despliegue, no el contenido de
conocimiento, los valores de secretos ni las acciones que una herramienta ya
ejecutó.

#### Previews con nombre

Crea un preview independiente para una rama o un pull request:

```bash
vatio push --env pr-42
vatio chat "What does Acme do?" --env pr-42
vatio tokens create --env pr-42
vatio publish --env pr-42
```

Los nombres usan minúsculas y dígitos separados por guiones o guiones bajos,
hasta 40 caracteres. Los previews compartidos de WhatsApp e Instagram siempre
usan el entorno literal `preview`.

La URL para compartir el preview que imprime `push` se abre sin login.
Cualquiera con esa URL puede hablar con el preview, así que compártela solo con
quienes deben probarlo.

#### Desplegar desde GitHub

Instala la GitHub App de Vatio y vincula un repositorio a tu workspace en la
consola. El dueño del workspace elige ese vínculo; el valor `workspace` de un
repositorio debe coincidir con él. Define el directorio del workspace si el
repositorio contiene más de un `vatio.yml`.

| Evento | Resultado |
|---|---|
| Pull request abierto, actualizado o reabierto | Un preview `pr-N` y un comentario con su URL cuando cambia el directorio del workspace |
| Pull request cerrado | Su preview se elimina |
| Push a la rama por defecto | Despliegue y publicación a live |

La CLI sigue disponible para despliegue manual y para diagnóstico.

#### Secretos

```bash
vatio secrets set ACME_API https://api.acme.com
vatio secrets set ACME_API_KEY YOUR_BACKEND_KEY
vatio secrets list
vatio secrets rm ACME_API_KEY
```

Las claves usan mayúsculas, dígitos y guiones bajos, empezando por una letra.
Las herramientas leen los valores con `$env.KEY`. El listado devuelve nombres,
nunca valores. Los cambios aplican a las llamadas siguientes sin desplegar, y
rigen en todos los entornos. El rollback no restaura valores antiguos.

Mantén las credenciales fuera de los manifiestos, el código de herramientas, el
conocimiento y los resultados de herramienta. La subida del workspace incluye
archivos comunes, así que guarda las claves privadas de firma fuera de ese
directorio.

### Solución de problemas

`https://docs.vatio.ai/es/troubleshooting`

| Síntoma | Qué revisar |
|---|---|
| La CLI no encuentra el workspace | Corre desde el directorio que contiene `vatio.yml` o debajo de él; inspecciona con `vatio doctor` |
| La autenticación de la CLI falla | Corre `vatio login`; verifica el host con `vatio doctor` |
| La validación del manifiesto o de una herramienta falla | Corre `vatio tools check` y arregla el archivo que reporta; las claves raíz desconocidas y la falta de `instructions` son errores |
| El agente nunca llama a una herramienta | Agrega su clave a `agent.tools`, describe cuándo usarla, pushea y empieza un chat de prueba nuevo |
| Faltan respuestas de conocimiento | Revisa `vatio kb show BASE`, las referencias en `knowledge` y la asignación de la herramienta `knowledge_lookup` |
| El agente no puede compartir una URL | Revisa que esté escrita literalmente en `links`, en `instructions` o en un resultado de herramienta — una URL armada o editada se elimina |
| El widget no aparece | Revisa `vatio widget`, el entorno del token, que haya un agente desplegado y el origen permitido exacto del sitio |
| Un visitante con sesión aparece como anónimo | Revisa que `auth.public_key` coincida con la clave con que firmas, y la firma, el `exp` y el `aud` del JWT (el slug del workspace) |
| WhatsApp o Instagram está conectado pero en silencio | Corre el `check` del canal; confirma la recepción del webhook y un agente en vivo; WhatsApp además debe estar activado |
| El agente dejó de responder en un chat | Revisa si hay traspaso a humano en la bandeja y libéralo cuando corresponda |

#### Reportes

Envía un reporte de error o una solicitud de funcionalidad desde la CLI:

```bash
vatio issue "Describe what happened and what you expected"
vatio issue --template > issue.md
vatio issue --file issue.md
vatio issue list
vatio issue show 42
vatio issue comment 42 "Additional details"
vatio issue "Push is rejected" --no-source
```

Corrido desde un workspace, `vatio issue` **adjunta el directorio** — los mismos
archivos que envía `vatio push`. Vatio conserva `vatio.yml` y todo lo que esté
bajo `tools/`, lista el resto por nombre, y registra qué opinaron sus propias
validaciones, así que un reporte sobre una herramienta llega con la herramienta.
Cualquier archivo del que una validación se haya quejado también se conserva. El
comando imprime qué va a adjuntar antes de enviar; `--no-source` manda el
reporte solo. Los archivos ocultos nunca se incluyen, así que `.env` y `.git/`
se quedan en tu máquina.

Los comandos de issue y comment **envían de inmediato**. Revisa el texto antes
de correrlos. Incluye el comando que falló, el error observado y un `request_id`
cuando esté disponible. La CLI incluye su versión y el contexto del workspace;
no adjunta la salida de tu comando anterior. Quita credenciales y datos de
clientes de cualquier reporte. Haz seguimiento en el hilo existente para que el
contexto se mantenga junto.

## CLI

### CLI

`https://docs.vatio.ai/es/cli/`

El paquete npm es `@vatio-ai/cli`; su ejecutable instalado es `vatio`. El resto
de esta referencia usa esa forma corta:

```bash
npm install -g @vatio-ai/cli@latest
vatio --help
```

Alternativamente, reemplaza `vatio` en cualquier comando por
`npx @vatio-ai/cli@latest`. Especificar `@latest` pide explícitamente la
release actual. Actualiza una instalación global corriendo el mismo comando de
instalación.

Autoriza la máquina una vez, y revisa con qué cree la CLI que está hablando:

```bash
vatio login
vatio doctor
```

Todos los comandos de abajo buscan el `vatio.yml` más cercano en el directorio
actual o por encima de él y leen `workspace` de ahí. Empieza por la
[guía rápida](https://docs.vatio.ai/es/quickstart) si todavía no has desplegado un agente.

#### Comandos

- [Workspace y cuenta](https://docs.vatio.ai/es/cli/workspace) — init, login, doctor, docs, config, issue.
- [Despliegues](https://docs.vatio.ai/es/cli/deploy) — tools check, push, publish, rollback, status, diff.
- [Chats](https://docs.vatio.ai/es/cli/chat) — chat, y sus vistas de transcripción y depuración.
- [Bases de conocimiento](https://docs.vatio.ai/es/cli/knowledge) — kb: bases, entradas y los sitios que las escriben.
- [Secretos](https://docs.vatio.ai/es/cli/secrets) — secrets: los valores que las herramientas leen como $env.KEY.
- [Tokens publicables](https://docs.vatio.ai/es/cli/tokens) — tokens y widget: lo que lleva una página.
- [Canales](https://docs.vatio.ai/es/cli/channels) — whatsapp e instagram, en vivo y en el preview compartido.
- [Claves de autenticación](https://docs.vatio.ai/es/cli/auth) — auth --new-key: el par que firma el JWT de un visitante.

#### Usarla

- [Configuración y subidas](https://docs.vatio.ai/es/cli/configuration) — Dónde viven las credenciales, qué variables las sobrescriben, qué envía un push.
- [Servidor MCP](https://docs.vatio.ai/es/cli/mcp) — vatio mcp expone estos mismos comandos a un agente de código.

### Workspace y cuenta

`https://docs.vatio.ai/es/cli/workspace`

| Comando | Propósito |
|---|---|
| `vatio init [SLUG] [--name NAME]` | Crear o usar el workspace remoto y escribir un manifiesto inicial en el directorio actual |
| `vatio login [--base-url URL]` | Autorizar esta máquina a través del navegador |
| `vatio logout` | Eliminar el token guardado |
| `vatio doctor` | Mostrar versión de Node, ubicación de la config, workspace y presencia de token |
| `vatio version` | Mostrar versiones de la CLI y de Node |
| `vatio docs [--save [PATH]]` | Traer la documentación; imprimirla o guardarla en `vatio-docs.md` / `PATH` |
| `vatio config show` / `get KEY` / `set KEY VALUE` / `unset KEY` | Administrar los ajustes locales `base_url` y `token` |

`init` escribe `vatio.yml` en el **directorio actual** y abre la autorización de
dispositivo si no has iniciado sesión. Todos los demás comandos buscan el
`vatio.yml` más cercano en el directorio actual o por encima de él y leen
`workspace` de ahí — no hay flag `--workspace` ni override `VATIO_WORKSPACE`.

Recurre a `doctor` antes que a nada cuando un comando no encuentra el workspace
o el token: imprime las cuatro cosas que lo deciden, y no imprime secretos, así
que es el seguro para pegar en un reporte.

#### Reportes

| Comando | Propósito |
|---|---|
| `vatio issue "message"` / `--file PATH` / `--template` / `--no-source` | Enviar un reporte o traer la plantilla; corrido desde un workspace adjunta el directorio, `--no-source` no |
| `vatio issue list` / `show ID` / `comment ID "reply"` | Leer y responder tus hilos de soporte |

Los comandos de issue y comment **envían de inmediato**. Revisa el texto antes
de correrlos, y mira [Solución de problemas](https://docs.vatio.ai/es/troubleshooting#reportes) para
qué incluir.

### Despliegues

`https://docs.vatio.ai/es/cli/deploy`

| Comando | Propósito |
|---|---|
| `vatio tools check` | Validar los archivos en la plataforma sin desplegar |
| `vatio push [--env NAME]` | Actualizar un preview; default `preview` |
| `vatio publish [--env NAME]` | Promover el preview nombrado a live; default `preview` |
| `vatio rollback` | Restaurar el despliegue live anterior |
| `vatio status` | Mostrar el estado del despliegue |
| `vatio diff [--env NAME]` | Comparar archivos locales con un despliegue; default `preview` |
| `vatio diff --stat` / `--name-only` / `--format json` / `--full` | Elegir el detalle del diff o el formato de salida |

`push` no puede apuntar a `live`: publicar es un paso aparte y deliberado.
`diff` compara tu **directorio local** con un despliegue, así que pushea las
ediciones locales antes de publicarlas.

`--env` acepta un [preview con nombre](https://docs.vatio.ai/es/deployments#previews-con-nombre)
además de `preview`, que es lo que le da a una rama o a un pull request su
propio entorno.

Ver [Despliegues](https://docs.vatio.ai/es/deployments) para qué contiene realmente un entorno, y
qué restaura y qué no un rollback.

### Chats

`https://docs.vatio.ai/es/cli/chat`

| Comando | Propósito |
|---|---|
| `vatio chat "message" [--env NAME] [--timeout N]` | Hablar como el portador del token de CLI; default `preview`, timeout 120 segundos |
| `vatio chat transcript [--env NAME] [--last N]` | Leer el chat actual de la CLI como lo vio el visitante; default 10 mensajes |
| `vatio chat debug [--env NAME] [--last N]` | El mismo chat con todo: llamadas a herramientas, mensajes borrados, identidad, entrega |
| `vatio chat reset [--env NAME]` | Empezar una conversación de CLI nueva con un contacto fresco |
| `vatio chat destroy CHAT_ID` | Borrar una conversación |

`debug` es al que hay que recurrir cuando una respuesta sale mal: muestra qué
herramientas corrieron, qué devolvieron, y si el workspace reconoció al
visitante.

El canal CLI identifica al desarrollador que tiene el token; no simula la
identidad de un cliente. Prueba en el canal real cuando lo que estés revisando
sea justamente la identidad o la entrega.

`chat --env live` crea una conversación live real — facturada, y visible en la
bandeja.

Los chats actuales se recuerdan por entorno en `.vatio-chat.json`. Agrega ese
archivo al `.gitignore` de tu repositorio.

### Bases de conocimiento

`https://docs.vatio.ai/es/cli/knowledge`

| Comando | Propósito |
|---|---|
| `vatio kb [list]` | Listar bases de conocimiento y referencias |
| `vatio kb show NAME` | Inspeccionar una base: sus sitios y sus entradas |
| `vatio kb create NAME` / `vatio kb rm NAME` | Crear o borrar una base sin referencias |
| `vatio kb write BASE ENTRY [FILE]` | Escribir una entrada, desde un archivo o stdin |
| `vatio kb cat BASE ENTRY` | Imprimir el Markdown guardado de una entrada |
| `vatio kb rm-entry BASE ENTRY` | Borrar una entrada |
| `vatio kb follow BASE URL` | Leer un sitio hacia la base, y de nuevo cada noche |
| `vatio kb unfollow BASE URL` | Dejar de leerlo, y descartar las entradas que escribió |
| `vatio kb refresh BASE [URL]` | Leer los sitios de nuevo ahora |
| `vatio kb reindex BASE [NAME]` | Re-crawlear una fuente o todas las fuentes de crawl |

`write` lee stdin cuando no se entrega archivo, que es lo que deja el ciclo de
edición en una línea:

```bash
vatio kb cat docs refunds | edit | vatio kb write docs refunds
```

`show` es el que responde "por qué no está respondiendo desde esto": reporta el
estado y los errores de cada sitio, y qué entradas están listas para buscarse.

**El conocimiento es compartido por todos los entornos**, así que escribir,
refrescar y borrar puede cambiar respuestas en vivo de inmediato — y un
rollback de despliegue no restaura contenido. Ver
[Bases de conocimiento](https://docs.vatio.ai/es/knowledge).

### Secretos

`https://docs.vatio.ai/es/cli/secrets`

| Comando | Propósito |
|---|---|
| `vatio secrets list` / `set KEY VALUE` / `rm KEY` | Administrar secretos compartidos del workspace |

Las claves usan mayúsculas, dígitos y guiones bajos, empezando por una letra.
Las herramientas leen los valores con `$env.KEY`.

`list` devuelve nombres, nunca valores. Los cambios aplican a las llamadas
siguientes sin desplegar, y rigen en todos los entornos — un rollback no
restaura un valor antiguo. Ver [Secretos](https://docs.vatio.ai/es/deployments#secretos).

Nunca pongas acá una clave privada de firma: ese almacén lo lee Vatio, y todo
el punto de la [autenticación](https://docs.vatio.ai/es/authentication/) es que Vatio guarde solo tu
clave pública.

### Tokens publicables y el widget

`https://docs.vatio.ai/es/cli/tokens`

| Comando | Propósito |
|---|---|
| `vatio tokens list` / `create [--env NAME] [--label NAME]` / `revoke PREFIX` | Administrar tokens publicables; `create` usa `live` por defecto |
| `vatio widget [--env NAME]` | Leer la configuración del widget y mostrar el comando de creación de token para ese entorno; default `live` |

`create` imprime el token completo una sola vez. `list` muestra solo prefijos,
que es además lo que recibe un revoke.

Revocar impide nuevos chats y listados de conversaciones con ese token; las
credenciales de chat ya emitidas siguen válidas hasta expirar.

`widget` es de solo lectura — `vatio.yml` gobierna cada campo que reporta, así
que un [`push`](https://docs.vatio.ai/es/cli/deploy) es lo que los cambia. Ver
[Widget web](https://docs.vatio.ai/es/channels/widget) para los campos mismos.

Nunca uses un token de desarrollador (`vat_…`) en un navegador. Los publicables
son los pensados para el código fuente de la página, y la lista de orígenes
permitidos es lo que los acota.

### Canales

`https://docs.vatio.ai/es/cli/channels`

#### WhatsApp

| Comando | Propósito |
|---|---|
| `vatio whatsapp [status]` | Inspeccionar tu número en vivo |
| `vatio whatsapp connect` / `check` / `activate` / `deactivate` / `disconnect` | Conectar, revisar o administrar el número en vivo |
| `vatio whatsapp numbers list` / `add PHONE` / `verify PHONE CODE` / `resend PHONE` / `remove PHONE` | Administrar teléfonos en el preview compartido |

Un número recién conectado queda en pausa: `check` reporta credenciales,
recepción de webhook y activación por separado, y `activate` es lo que arranca
las respuestas en vivo. `numbers add` no necesita cuenta de Meta — el número
compartido del preview es cómo pruebas en un minuto. Ver
[WhatsApp](https://docs.vatio.ai/es/channels/whatsapp).

#### Instagram

| Comando | Propósito |
|---|---|
| `vatio instagram [status]` | Inspeccionar tu cuenta en vivo |
| `vatio instagram connect` / `check` / `disconnect` | Conectar, revisar o desconectar la cuenta en vivo |
| `vatio instagram accounts list` / `add @HANDLE` / `verify CODE` / `resend ID` / `remove ID` | Administrar cuentas en el preview compartido |

Un workspace conecta una cuenta y una cuenta se conecta a un workspace;
`disconnect` antes de cambiar. Ver [Instagram](https://docs.vatio.ai/es/channels/instagram).

`connect` en cualquiera de los dos canales abre el navegador para el
consentimiento de Meta, así que necesita al titular de la cuenta — es la única
parte de esto que un agente de código no puede hacer por ti.

### Claves de autenticación

`https://docs.vatio.ai/es/cli/auth`

| Comando | Propósito |
|---|---|
| `vatio auth --new-key` | Generar el par de claves que firma tu JWT, e imprimir el bloque `auth:` y los claims a usar |

Escribe dos archivos, y van a lugares distintos. `identity.pub` se queda en el
workspace y se commitea como cualquier otro archivo; `identity.pem` va a tu
propio backend como secreto, y conviene borrarlo del directorio del workspace
una vez cargado allá.

El comando imprime el bloque `auth:` para pegar en `vatio.yml` y los claims a
firmar, así que su salida es la mayor parte de la configuración.
[Autenticación](https://docs.vatio.ai/es/authentication/) tiene el resto, incluyendo por qué `aud`
es requerido y qué ve una herramienta privada.

### Configuración y subidas

`https://docs.vatio.ai/es/cli/configuration`

`~/.vatio/config.json` guarda `base_url` y `token`. Las variables de entorno
sobrescriben los ajustes guardados: `VATIO_BASE_URL`, `VATIO_TOKEN` y
`VATIO_HOME` (el directorio de configuración). `config show` puede imprimir el
token guardado; usa `doctor` cuando compartas diagnósticos.

`tools check`, `diff` y `push` envían los archivos del workspace a la plataforma
para validarlos. Requieren acceso a red y autenticación. `push` crea el remoto
si falta; los otros dos requieren un workspace existente.

Las subidas excluyen archivos y directorios ocultos, `node_modules`, `tmp` y
`log`. Los límites son 500 archivos, 2 MB por archivo y 8 MB en total. Estas
exclusiones no hacen que un archivo común sea seguro para guardar credenciales.

### Conectar MCP

`https://docs.vatio.ai/es/cli/mcp`

Vatio provee un servidor MCP sobre stdio. En la configuración de servidores de
tu cliente MCP, usa este comando y estos argumentos:

```json
{
  "mcpServers": {
    "vatio": {
      "command": "vatio",
      "args": ["mcp"]
    }
  }
}
```

Usa el formato de configuración de tu cliente si difiere. Si tu cliente no
encuentra `vatio` en su `PATH`, usa `"command": "npx"` con
`"args": ["-y", "@vatio-ai/cli@latest", "mcp"]`. Inicia sesión con la CLI
primero. Arranca el servidor en el directorio del workspace, o pasa la ruta
absoluta del workspace como `workspace_dir` en las llamadas a herramientas:

```json
{
  "args": ["--env", "pr-42"],
  "workspace_dir": "/path/to/support-agent"
}
```

Eso es una llamada a `vatio_push`. Cada herramienta MCP toma los argumentos que
van después de su comando de CLI como un arreglo de strings. Un mensaje con
espacios es un solo elemento del arreglo.

#### Qué expone

MCP expone docs, diagnóstico, validación, despliegue, chat, conocimiento,
secretos, tokens, inspección del widget y administración de canales. El login,
la creación de workspaces, el envío de issues, la configuración, la
conexión/desconexión de canales, el borrado de chats y el borrado de bases de
conocimiento usan la CLI directamente. El consentimiento en el navegador y los
códigos de verificación requieren al titular de la cuenta.

#### Límites

La salida de un comando MCP está limitada a 20.000 caracteres. Para la
documentación completa, usa `vatio_docs` con
`args: ["--save", "/absolute/path/vatio-docs.md"]` y lee ese archivo con las
herramientas de archivos de tu agente de código. Los comandos expiran a los
180 segundos.

## SDK

### SDK de navegador

`https://docs.vatio.ai/es/sdk/`

El SDK de navegador es un paquete npm sin dependencias y con tipos incluidos.
Crea un token publicable y permite el origen de tu página como se describe en
[la guía del widget](https://docs.vatio.ai/es/channels/widget).

```sh
npm install @vatio-ai/sdk
```

```js
import { Vatio } from "@vatio-ai/sdk";

const options = { workspace: "acme", token: "vatpub_REPLACE_ME" };
const chat = await Vatio.chat(options);
const messages = new Map();

function receive(message) {
  messages.set(message.id, message);
  // Renderiza los mensajes ordenados por ID, escapando contenido no confiable.
}

chat.on("message", receive);
chat.on("typing", (typing) => { /* Actualiza tu indicador de escritura. */ });
chat.on("status", (status) => { /* Actualiza tu indicador de conexión. */ });
chat.on("error", (error) => console.error(error.code, error.message));

for (const message of await chat.history()) receive(message);
await chat.send("What does Acme do?");
// Llama a chat.close() cuando esta vista se desmonte.
```

#### Sin bundler

Cualquier CDN de npm sirve el mismo módulo:

```html
<script type="module">
  import { Vatio } from "https://cdn.jsdelivr.net/npm/@vatio-ai/sdk/+esm";
</script>
```

Fija el major de la forma habitual — `"@vatio-ai/sdk": "^2.0.0"`. El SDK solía
publicarse desde una URL versionada (`cdn.vatio.ai/v1/sdk.js`) por exactamente
esta razón, y un rango semver lo dice mejor: un cambio incompatible es un major
nuevo al que actualizas cuando decides, no una segunda URL de la que enterarte.

Sigue: la [referencia](https://docs.vatio.ai/es/sdk/reference) de cada método y opción, y los
[límites](https://docs.vatio.ai/es/sdk/limits) que toda página encuentra.

### Referencia del SDK

`https://docs.vatio.ai/es/sdk/reference`

`Vatio.chat` inicia o retoma una conversación y espera una suscripción antes de
resolver. El SDK maneja reconexiones, resincronización del historial y fallback
a polling. Los mensajes incluyen `id`, `role` y `content`. Deduplica el
historial y la entrega de eventos por `id`.

| Método | Propósito |
|---|---|
| `Vatio.config({ workspace, token })` | Traer la configuración pública del agente y del widget |
| `Vatio.chat(options)` | Iniciar, retomar o reabrir una conversación |
| `Vatio.conversations({ workspace, token, ... })` | Listar las conversaciones de este visitante, más nuevas primero |
| `Vatio.resumable({ workspace, ... })` | Revisar si hay una conversación guardada localmente y no expirada |
| `Vatio.reset({ workspace, ... })` | Olvidar la conversación guardada localmente; no borra el historial del servidor |
| `chat.send(text)` | Enviar un mensaje |
| `chat.history({ limit })` | Traer mensajes |
| `chat.on(event, handler)` | Suscribirse a `message`, `typing`, `status` o `error` |
| `chat.close()` | Desconectar el cliente |

#### Opciones

`Vatio.chat` acepta `workspace` y `token`, más los opcionales `baseUrl`,
`fresh`, `scope`, `visitorToken`, `replyStyle` y `conversation`. `fresh: true`
empieza un chat nuevo. `conversation` reabre una entrada devuelta por
`Vatio.conversations`.

Usa un `scope` distinto cuando las UIs de preview y live comparten origen. Pasa
el mismo `scope` y `visitorToken` a `chat`, `conversations`, `resumable` y
`reset`. El almacenamiento se separa por el token de visitante completo, así que
emitir un token nuevo también cambia su scope local de conversación. No fusiona
contactos entre dispositivos.

#### Almacenamiento

El SDK guarda la conversación actual por pestaña y una referencia de visitante
en local storage. Las credenciales de chat duran 12 horas. Trata las
referencias de visitante y las credenciales de chat como privadas: la referencia
puede recuperar la lista de conversaciones de ese visitante y credenciales de
chat frescas, así que guarda la referencia donde el usuario con sesión no pueda
leer la de otro, y nunca la derives de un ID de usuario público o predecible.
Emite una referencia nueva cuando cambie el usuario con sesión.

### Límites y entrega

`https://docs.vatio.ai/es/sdk/limits`

#### Límites

Los mensajes están limitados a 4.000 caracteres. Por IP de navegador y
workspace, por minuto: 10 conversaciones iniciadas, 30 mensajes enviados, 60
lecturas de configuración y 60 listados de conversaciones. Sobre el límite, el
SDK emite un evento `error` y la petición subyacente responde HTTP 429; haz
backoff en vez de reintentar de inmediato.

Cada petición que hace el SDK se revisa contra la lista de orígenes permitidos
del workspace, así que un token publicable sacado de tu HTML es inútil desde
otra página. La lista es lo que mantiene un token en tu propio sitio; no es
evidencia de quién es el visitante. Para decirle al agente quién tiene la sesión
iniciada, ver [Sesiones y canales](https://docs.vatio.ai/es/authentication/sessions).

#### Estilo de respuesta

| `replyStyle` | Entrega |
|---|---|
| `stream` | Default en web: una respuesta completa, sin demora artificial |
| `paced` | Mensajes cortos con indicadores de escritura y pausas |
| `instant` | Una respuesta completa sin eventos de escritura |

`stream` no es streaming token a token por la red. Renderiza progresivamente la
respuesta recibida si quieres. El estilo queda fijo al crear el chat; usa
`fresh: true` para cambiarlo.

Usa el SDK para entrega en tiempo real. El protocolo de socket subyacente no es
un contrato de integración público.

## Referencia de API

### Referencia de API

`https://docs.vatio.ai/es/api/`

URL base: `https://vatio.ai`. Todos los cuerpos de petición de abajo son JSON;
envía `Content-Type: application/json` en las peticiones con cuerpo. Reemplaza
`:slug`, `:id` y los demás placeholders de ruta por el valor real.

Tus clientes no usan esta API. Un navegador habla con Vatio a través del
[SDK](https://docs.vatio.ai/es/sdk/) con un token publicable, sobre una superficie separada que
expone una conversación y nada más del workspace.

#### Obtener un token

Toda petición de abajo se autoriza con un **token de desarrollador**, que se ve
como `vat_…`. No hay una página en la consola de donde copiarlo: un token de
desarrollador se emite por autorización de dispositivo, donde una persona
aprueba esta máquina en el navegador, una vez.

Inicia el flujo y lee `device_code`, `user_code`, `verification_uri_complete` e
`interval` de la respuesta:

```http
POST /cli/device_authorizations
Content-Type: application/json
```

Manda a la persona a `verification_uri_complete`, y después consulta cada
`interval` segundos hasta que responda con el token en vez de
`authorization_pending`:

```http
POST /cli/device_authorizations/token
Content-Type: application/json

{ "device_code": "..." }
```

Ninguna de las dos rutas tiene alcance de workspace ni recibe token — son cómo
obtienes uno. [Autorización de dispositivo](https://docs.vatio.ai/es/api/device-authorization) tiene
el flujo completo, incluyendo cómo listar y crear workspaces con el resultado.

Si tienes una terminal a mano, `vatio login` hace exactamente estas dos
llamadas y escribe el token en `~/.vatio/config.json`, de donde puedes leerlo.

#### Credenciales

Envía el token como `Authorization: Bearer TOKEN`. Toda ruta de abajo es
relativa a `/api/v1/:slug`, donde `:slug` es el workspace.

Un token alcanza todos los workspaces de los que su usuario es dueño. Puede
desplegar, leer secretos, iniciar conversaciones y emitir tokens publicables,
así que pertenece a un servidor o a un secreto de CI — nunca a una página, una
app móvil o un repositorio.

Los errores llevan `error_key` y `error_message`, o `error` y
`error_description` en fallas de autenticación. Ver [Errores](https://docs.vatio.ai/es/api/errors).

#### Endpoints

- [Despliegues](https://docs.vatio.ai/es/api/deployments) — Validar, desplegar, publicar, revertir y leer el historial de despliegues.
- [Secretos](https://docs.vatio.ai/es/api/secrets) — Secretos de workspace de solo escritura que tus herramientas leen como $env.KEY.
- [Bases de conocimiento](https://docs.vatio.ai/es/api/knowledge) — Bases, entradas y los sitios que las escriben.
- [Tokens publicables](https://docs.vatio.ai/es/api/tokens) — Emite y revoca los tokens vatpub_ que lleva una página.
- [Canales](https://docs.vatio.ai/es/api/channels) — Conexiones de WhatsApp e Instagram, y registros del preview compartido.
- [Chats](https://docs.vatio.ai/es/api/chats) — Tus propias conversaciones de desarrollador, con la vista de depuración completa.
- [Verificación de teléfono](https://docs.vatio.ai/es/api/phone-verification) — OTP por WhatsApp para tu propio backend, sin agente de por medio.

### Errores

`https://docs.vatio.ai/es/api/errors`

Los errores de autenticación y de acceso normalmente devuelven:

```json
{
  "error": "workspace_forbidden",
  "error_description": "Token does not have access to this workspace",
  "request_id": "REQUEST_ID"
}
```

HTTP 401 significa credenciales inválidas; 403, permiso faltante u origen no
permitido; 404, un recurso inexistente. La validación normalmente usa 422.
Los errores de despliegue y de chat pueden usar `error_key` y `error_message`.
Los errores del servicio de verificación de teléfono usan
`{ "error": { "code": "...", "message": "..." } }`.
Conserva `request_id` cuando la respuesta lo incluya.

### Autorización de dispositivo

`https://docs.vatio.ai/es/api/device-authorization`

`vatio login` es la ruta normal, y estos endpoints son cómo funciona. No tienen
alcance de workspace y no reciben token de desarrollador — son cómo obtienes uno.

Una CLI propia inicia la autorización de dispositivo con
`POST /cli/device_authorizations`. La respuesta entrega `device_code`,
`user_code`, URLs de verificación, `interval` y `expires_in`.

Haz que el usuario abra `verification_uri_complete`, y después consulta
`POST /cli/device_authorizations/token` con `{ "device_code": "..." }` en el
intervalo indicado. `GET /cli/workspaces` lista los workspaces y
`POST /cli/workspaces` crea uno con `slug` y un `name` opcional, usando el token
de desarrollador resultante.

### API de despliegues

`https://docs.vatio.ai/es/api/deployments`

| Método | Ruta | Propósito |
|---|---|---|
| POST | `/deploy/check` | Validar un bundle de archivos y devolver manifiesto y diff |
| PATCH | `/deploy/preview` | Desplegar el manifiesto validado a un preview |
| POST | `/deploy/publish` | Promover un preview a live |
| POST | `/deploy/rollback` | Restaurar el live anterior |
| GET | `/deploy/status` | Punteros de despliegue |
| GET | `/deploy/manifest?environment=preview` | Un manifiesto desplegado |
| GET | `/deploy/revisions` | Historial de despliegues |
| GET | `/deploy/revisions/:id` | Un despliegue |

Primero llama a `POST /deploy/check` con los archivos del workspace:

```json
{
  "environment": "preview",
  "files": [
    {
      "path": "vatio.yml",
      "encoding": "utf-8",
      "content": "workspace: acme\nagent:\n  instructions: Help visitors learn about Acme.\n"
    }
  ]
}
```

Cada archivo tiene un `path` relativo, `content` y `encoding` (`utf-8` o
`base64`). La respuesta contiene `ok`, `manifest`, `errors`, `warnings` y
`diff`. Los diagnósticos contienen `message` y `path`. Una falla de validación
es HTTP 200 con `ok: false`; un bundle ilegible o sobredimensionado es 422.

Cuando `ok` es true, envía el `manifest` devuelto sin modificar a
`PATCH /deploy/preview`, con `git_sha` y `environment` opcionales (default
`preview`). No construyas tú el manifiesto normalizado. La respuesta incluye
`deployment_id`, `environment`, `share_url` y `preview_url`.

Publica con `{ "environment": "preview" }`, o nombra otro preview. El rollback
no necesita cuerpo. Ninguna de las dos operaciones sube tus archivos locales.

### API de secretos

`https://docs.vatio.ai/es/api/secrets`

| Método | Ruta | Propósito |
|---|---|---|
| GET | `/deploy/secrets` | Nombres de secretos |
| PATCH | `/deploy/secrets/:key` | Definir un secreto con `{ "value": "..." }` |
| DELETE | `/deploy/secrets/:key` | Eliminar un secreto |

Los valores son de solo escritura: leer lista claves, nunca contenidos.

### API de bases de conocimiento

`https://docs.vatio.ai/es/api/knowledge`

| Método | Ruta | Cuerpo o resultado |
|---|---|---|
| GET / POST | `/knowledge_bases` | Listar bases / crear con `{ "name": "docs" }` |
| GET / DELETE | `/knowledge_bases/:name` | Leer una base / borrarla cuando no tiene referencias |
| PATCH | `/knowledge_bases/:name/entries/:entry` | `{ "content": "# Refunds\n..." }` — crea la entrada o la reemplaza |
| GET | `/knowledge_bases/:name/entries/:entry` | La entrada, con su Markdown guardado |
| POST | `/knowledge_bases/:name/sites` | `{ "url": "acme.com/help/**" }` |
| DELETE | `/knowledge_bases/:name/sources/:source` | Borrar una fuente y sus entradas |
| POST | `/knowledge_bases/:name/sources/:source/reindex` | Encolar un crawl; las subidas no se pueden reindexar |

### API de tokens publicables

`https://docs.vatio.ai/es/api/tokens`

| Método | Ruta | Cuerpo o resultado |
|---|---|---|
| GET | `/publishable_tokens` | Listar prefijos de tokens activos creados por desarrolladores |
| POST | `/publishable_tokens` | `{ "environment": "live", "label": "website" }`; devuelve el token completo una sola vez |
| DELETE | `/publishable_tokens/:prefix` | Revocar un token |
| GET | `/widget` | Configuración del widget, lista de orígenes, disponibilidad del agente y prefijos de tokens |

Los entornos de tokens publicables aceptan previews con nombre además de `live`
y `preview`; omitirlo usa `live`. `GET /widget` es de solo lectura — `vatio.yml`
gobierna cada campo que reporta, así que un push es lo que los cambia.

### API de canales

`https://docs.vatio.ai/es/api/channels`

| Método | Ruta | Propósito / cuerpo |
|---|---|---|
| GET / POST / DELETE | `/whatsapp_account` | Estado / obtener URL de conexión en el navegador / desconectar |
| POST | `/whatsapp_account/check` | Revisar credenciales y reparar la suscripción del webhook |
| POST | `/whatsapp_account/activate` | Habilitar respuestas en vivo |
| POST | `/whatsapp_account/deactivate` | Pausar respuestas en vivo |
| GET / POST / DELETE | `/instagram_account` | Estado / obtener URL de consentimiento / desconectar |
| POST | `/instagram_account/check` | Revisar credenciales y reparar la suscripción del webhook |
| GET / POST | `/test_phone_numbers` | Listar / registrar con `{ "phone_number": "+56912345678" }` |
| POST | `/test_phone_numbers/:wa_id/verify` | `{ "code": "123456" }` |
| POST | `/test_phone_numbers/:wa_id/resend` | Reenviar un código |
| DELETE | `/test_phone_numbers/:wa_id` | Eliminar un teléfono de prueba |
| GET / POST | `/test_instagram_accounts` | Listar / declarar con `{ "username": "yourhandle" }` |
| GET / DELETE | `/test_instagram_accounts/:id` | Leer / eliminar una cuenta de prueba |
| POST | `/test_instagram_accounts/verify` | `{ "code": "123456" }` |
| POST | `/test_instagram_accounts/:id/resend_otp` | Reenviar después del primer DM de la cuenta |

`:wa_id` son los dígitos del teléfono sin `+`. Registrar un teléfono ya
verificado en este workspace funciona sin enviar código. Un teléfono verificado
en otro workspace tuyo se puede mover acá. Los códigos llegan por el canal y la
API nunca los devuelve.

### API de chats

`https://docs.vatio.ai/es/api/chats`

Un chat acá eres tú, el desarrollador que tiene el token, en el canal `cli`. No
es una forma de suplantar a un cliente: `channel`, `from`, `as`, `session_id`,
`reply_style`, `email` y `phone_number` se rechazan todos, y `environment` es el
único campo que `create` y `reset` aceptan.

| Método | Ruta | Cuerpo / query |
|---|---|---|
| POST | `/chats` | `{ "environment": "preview" }` → `chat_id` |
| GET | `/chats/:id` | La conversación y su estado de identidad |
| POST | `/chats/:id/reset` | `{ "environment": "preview" }` → un `chat_id` nuevo |
| DELETE | `/chats/:id` | Borrar la conversación |
| POST | `/chats/:id/messages` | `{ "content": "Hello" }` → 202 con `user_message_id` |
| GET | `/chats/:id/messages` | `after` y `limit` |

**`environment` es requerido en `create` y `reset`**, y es el único campo de
esta API sin default. En todo el resto, omitir el entorno cae en algo inofensivo;
acá el fallback sería `live`, y una petición a la que le falta una palabra sería
una conversación real con tu agente publicado — facturada, visible en la bandeja
y corriendo tus herramientas contra secretos de producción. Envía `preview` para
hablar con un despliegue de preview, o `live` cuando lo digas en serio. Un
nombre que no es un entorno es 422, no una suposición.

Las respuestas son asíncronas: publica un mensaje y después consulta
`GET /chats/:id/messages` con `after` apuntando al último id que hayas visto.

Ambos endpoints de lectura siempre devuelven la vista de desarrollador, porque
el token ya es la vista de desarrollador — mensajes borrados, llamadas a
herramientas con sus resultados, adjuntos, acuses de entrega del canal y un
bloque `identity` que dice si el workspace reconoció al visitante y por qué no.
No hay parámetro de vista que elegir; imprime tanto del payload como necesite
quien llama.

Esta API solo ve las conversaciones que ella misma inició. Para leer lo que
dijeron clientes reales en el widget, WhatsApp o Instagram, usa
[la bandeja](https://docs.vatio.ai/es/channels/inbox).

### API de verificación de teléfono

`https://docs.vatio.ai/es/api/phone-verification`

Sin comando de CLI: esta es para tu propio backend, para verificar un teléfono
mediante OTP por WhatsApp. Se requiere un workspace existente y un remitente de
WhatsApp; un despliegue de agente no. No autentica a un visitante dentro de una
conversación con el agente.

```http
POST /api/v1/:slug/phone_verifications
Content-Type: application/json
Authorization: Bearer VATIO_DEVELOPER_TOKEN

{ "phone_number": "+56912345678" }
```

La respuesta 201 contiene `verification_id` y `expires_at`. La entrega se
encola; un 201 no confirma que el código llegó al teléfono. Durante el período
de espera para reenvío, la respuesta es 429 con `verification_id`, `expires_at`
y `retry_after_seconds`.

```http
POST /api/v1/:slug/phone_verifications/:id/verify
Content-Type: application/json
Authorization: Bearer VATIO_DEVELOPER_TOKEN

{ "code": "123456" }
```

El éxito devuelve `{ "verified": true }`. Un código inválido devuelve 422 con
`verified: false` y `attempts_left`; cinco intentos fallidos bloquean la
verificación.

Un workspace dedicado a esta API declara `agent: false` — ver
[el manifiesto](https://docs.vatio.ai/es/manifest#claves-raiz).
