---
name: flujo-chatbot
description: "Build a Chatbot Flow («flujo chatbot», frepi.flow/v1): a cheap first pass of buttons, a WhatsApp form, rules and a small model for free text, before an agent in Frepi or a bot outside it; only qualified people reach the agent or bot. Several flows in order, test, a person activates. Spanish triggers: flujo chatbot, flujo de entrada, calificar leads."
license: "Proprietary. Free to use with a Frepi account. See LICENSE."
metadata:
  author: "frepi-ai"
  version: "2"
---

# Flujo chatbot

Un **flujo chatbot** es la primera pasada barata de un agente: hace preguntas con botones, manda un formulario de
WhatsApp, aplica reglas fijas y, solo para el texto libre, usa un modelo pequeño. Al agente o al bot le pasa solo a
quien califica; quien no califica recibe un cierre y casi no cuesta. Es **una regla más del agente**, y tiene dos casas:

- **(A) un agente que vive en Frepi** (agente de línea): los flujos van en su documento `frepi.agent/v1`, sección
  `flows` (`bi_agent_config_get` / `bi_agent_config_apply`, skill `agentes`).
- **(B) un bot que vive fuera de Frepi** (en la IA del dueño): los flujos van en `chatbot_setup`, campo `flows`, y se
  leen con `chatbot_settings` (skill `configurar-bot`).

Cada flujo es **un documento JSON**, `frepi.flow/v1`, guardado por versiones: cada versión se guarda, se prueba y
**una persona** la activa. El nombre anterior de este skill, `flujo-de-entrada`, sigue funcionando.

Los nombres con `bi_` son los de MCP; en el copiloto de Frepi son los mismos sin `bi_`. Las herramientas
`chatbot_*` se llaman sin prefijo.

Referencias: [esquema](references/esquema.md) (el JSON Schema, los operandos, la entrada, el traspaso y los límites) y
[ejemplos](references/ejemplos.md) (seis flujos, uno de bot y una segunda situación). Cárgalas con
`bi_get_skill(name="flujo-chatbot", reference="esquema")`.

## Antes de empezar

- **¿Agente de Frepi o bot?** Un agente que se configura en la página «Agentes» es (A). Un bot que corre en el
  asistente de IA del propio dueño o en un script suyo es (B). Si no está claro cuál, pregúntalo.
- **Las herramientas.** (A) `bi_agent_config_get`, `bi_agent_config_apply`, `bi_agent_test`. (B) `chatbot_setup` y
  `chatbot_settings`. Si no están, para aquí: dile a la persona que recargue el conector de Frepi en su cliente de IA y
  que empiece de nuevo. Las cuatro herramientas de flujo de antes (leer, guardar, simular, activar) ya no existen: todo va por el
  documento del agente (A) o por `chatbot_setup` (B).
- **Las líneas.** (A) `bi_list_line_agents` → el `persona_id` y los `line_id`; un flujo solo cubre líneas en que ese
  agente contesta (`flow_lines_invalid`). (B) las líneas del flujo son de las del bot, y el bot tiene que estar en
  **Leer y enviar**.
- `FEATURE_DISABLED` / `agent_doc_flows_off` = los flujos no están activos en este workspace: dilo y para.
- El flujo se arma para **este** dueño: su texto nombra a su agente o a su bot («Te paso con Ana»), así que un flujo no
  se pasa a otro dueño.

## 1. Cuándo conviene un flujo

- Mucha gente que no va a comprar, y cada respuesta del agente (o cada vuelta del modelo del bot) cuesta. El flujo
  filtra con botones y reglas; **el modelo solo ve a quien califica**.
- Hay una regla clara para calificar (edad, tipo de cliente, una lista, un monto) que se pide con botones o un
  formulario.
- No conviene si el negocio necesita conversar desde el primer mensaje, o si no hay regla: ahí basta el agente o el bot.

## 2. Varios flujos, cada uno con su situación

Un agente o un bot puede tener **hasta 10 flujos**. Cada uno tiene:

- **su situación** (`when`): quién entra (contacto nuevo o cualquiera, una etiqueta, un campo, palabras del primer
  mensaje, una sola vez o de nuevo pasados N días) y, opcional, `skip_if_answered_hours` (de 1 a 720);
- **sus líneas**: las líneas del dueño en que corre;
- **su lugar en el orden**: en cada mensaje entra **el primero, de arriba abajo, cuya situación se cumple**. Si ninguno
  se cumple, el chat sigue como siempre (contesta el agente o el bot).

Lo que cambia lo que dices:

- **`skip_if_answered_hours`** = «no empieces si el agente o el bot ya le contestó a este chat en las últimas N horas».
  Ausente = empieza igual; no es una regla dura, la decide el dueño. Si no empieza, se prueba el siguiente flujo.
- **Un solo dueño por línea.** En una línea, los flujos vivos son de un solo dueño: el agente que contesta, o un bot.
  Activar o reanudar un flujo en una línea donde ya hay flujos vivos de otro bot responde `flow_line_taken` /
  `FLOW_LINE_TAKEN`. Una línea con agente no puede llevar bot.
- **Un chat tiene un solo flujo vivo a la vez.** Quien está a mitad de un flujo termina con la versión con la que
  empezó, aunque cambies el flujo.
- Un flujo se puede **pausar** (vuelve el agente o el bot) y **quitar** (se guardan sus versiones). Un flujo que se
  queda sin líneas se quita solo.

## 3. Entrevista, una pregunta a la vez

Pregunta **una cosa a la vez**, en el idioma de la persona. En el copiloto usa `ask_user` con opciones. Las tres
primeras son siempre estas, con estas palabras:

1. «1. ¿Quién entra al flujo?» — opciones «Contactos nuevos, una vez» · «Todos» · «Con una etiqueta».
   Contactos nuevos, una vez: `when.contact = "new"`, `once_per_contact: true`. Todos: `"contact": "any"` y
   `once_per_contact: false`. Con una etiqueta: `has_tag` (y pregunta cuál; tiene que existir).
2. «2. Si no califica, ¿qué pasa?» — opciones «Mensaje de cierre y etiqueta» · «Pasa al equipo».
   Lo primero es un paso `end` con `text`, `tag` y, si la persona quiere, `agent_quiet_days` (sección 6). Lo segundo es
   un `handoff` con `"to": "team"`.
3. «3. ¿Cuál es la regla para calificar?» — texto libre. Tradúcela a un paso `rule` (sección 4).

Después, una a la vez:

4. **Qué pregunta y cómo.** Botones (hasta 3, de 1 a 20 caracteres) para opciones cerradas; un formulario de WhatsApp
   para datos (fecha, listas). El formulario tiene que existir en «Formularios» y estar publicado en la línea.
5. **Si no responde.** Cada pregunta y formulario espera (`wait`, de 1m a 24h) y luego va a `on_timeout`: casi siempre
   un `end` con su etiqueta. **Un `handoff` al agente o al bot nunca se alcanza solo por tiempo**
   (`handoff_agent_on_timeout`).
6. **Etiquetas.** Qué etiqueta pone cada final y cada traspaso. Solo etiquetas que ya existen en el workspace.
7. **Texto libre.** Solo si la persona lo pide: una pregunta con botones puede mandar lo que se escribe a mano a un
   paso `ai_pick` por `on_other`. La IA lo ubica en una de sus opciones; si duda, repregunta (`retries`,
   `retry_text`) y al final va a `on_unsure` (casi siempre el equipo). Cuesta créditos en cada uso: dilo.
8. **¿Hay otra situación?** («¿Los clientes que vuelven entran distinto?»). Cada una es otro flujo: pregunta sus
   líneas, su lugar en el orden y, si hace falta, `skip_if_answered_hours`.

## 4. Armar el JSON

Empieza desde un ejemplo de [ejemplos](references/ejemplos.md). Reglas:

- `when.event` es siempre `"inbound"`. `start` nombra el primer paso. Los pasos tienen nombres `[a-z][a-z0-9_]`.
- Pasos: `question` (texto + `buttons` {texto del botón: paso}), `form` (`form`, `button`, `on_complete`), `text`
  (con `next` opcional), `rule` (`all` o `any` de condiciones, `then`, `else`), `ai_pick` (`options`
  {opción: paso}, `on_unsure`), `handoff` (`to`, `text` y `tag` opcionales), `end` (`label` obligatorio de hasta 30,
  `text`, `tag`, `agent_quiet_days` de 1 a 180).
- **`handoff.to` depende del dueño.** Un agente de Frepi: `agent` o `team`. Un bot: `bot` o `team`. Cualquier otro
  es `handoff_target_invalid`.
- Una condición es `[operando, operador, valor]`. Operandos: `answers.<campo>`, `picks.<paso>`, `fields.<clave>`,
  `age(<operando>)`. Operadores: `eq ne lt lte gt gte in not_in empty not_empty contains`. El valor puede ser
  `{"var": "<nombre>"}` de `vars`. Un operando que falta hace falsa la condición.
- `then_label` / `else_label` y `note` (hasta 120) son lo que muestra el árbol.
- Sin ciclos, todo paso alcanzable desde `start`, a lo sumo 20 pasos y 3 `ai_pick`.
- A quien recibe el chat le llegan las respuestas del formulario y lo que se eligió en cada botón y en la IA.

## 5. Guardar, probar y pedir que lo activen

**(A) Un agente de Frepi.**

1. `bi_agent_config_get(persona_id)`: el documento y, aparte, el bloque `flows` (estado, versiones pendientes,
   conteos de 7 días).
2. Pon el flujo en la sección `flows` del documento:
   `{"id": null, "on": false, "lines": [<line_id>], "document": {…}, "cases": [...], "changeNote": "…"}`. `id` null =
   flujo nuevo. La lista es **el orden**. La sección presente es la lista completa: un flujo que dejas fuera se quita.
   Ausente = no se toca. Un flujo que ya existe se cambia en su sitio (copia el que exportó `bi_agent_config_get`).
3. `bi_agent_config_apply(document)` → lee la tarjeta a la persona → con su «sí», la misma llamada con
   `confirmation_token`.
4. `bi_agent_test(persona_id, mode="flow", flow_id, cases?)` corre los casos sobre la versión nueva (hasta 30 casos).
   Si uno falla, corrige y vuelve al 2.
5. **Pide a una persona que lo active.** Nunca por tu cuenta (sección 7).

**(B) Un bot de fuera.**

1. `chatbot_settings(flow_documents=true)`: los flujos del bot, cada uno con su documento.
2. `chatbot_setup` con `flows`: la lista completa en orden, cada uno `{flow_id, on, lines, document, change_note?,
   cases?}` (`flow_id` null = nuevo). Una llamada que solo trae `flows` no cambia los demás ajustes del bot.
3. La tarjeta de `chatbot_setup` corre los casos y muestra «pasa p de t casos» por flujo, y el dueño la aprueba. Si
   cambias algo después de verla, la segunda llamada responde `FLOW_PREVIEW_STALE`: pide una tarjeta nueva. Probar
   cuesta créditos (hasta 20 usos del modelo por persona y por hora).
4. **Pide al dueño que lo active** en la página del bot (sección 7).

`cases`: uno por camino (califica, no califica, no responde, texto libre), cada uno `{label, script, expect}`.
`script` es lo que escribe el cliente: `{"kind": "text", "text": …}`, `{"kind": "tap", "button": …}`,
`{"kind": "form", "answers": {…}}`; el primero es el mensaje con que llega. `expect` es `agent`, `bot`, `team`, `none`
(el flujo sigue esperando) o `end:<paso>`. Un `label` igual reemplaza el caso. **Un script nunca lleva un teléfono**
(`flow_case_phone`). Cierra con: los pasos en orden, dónde se usa la IA y «Creé {n} casos y los {n} pasan.».

## 6. Qué pasa con el chat

- **Traspaso.** `to: agent` = contesta el agente. `to: bot` = el chat pasa a la lista del bot con el resultado del
  flujo y las respuestas, y el bot **no vuelve a preguntar** lo que ya se respondió. `to: team` = una persona; en un
  bot es su traspaso de siempre, con su aviso por correo.
- **Si una persona escribe, el flujo termina** («interrumpido por una persona»).
- **Si el bot responde, el flujo termina** («interrumpido por el bot»). No hay candado: el bot puede contestar
  cualquier chat, uno que está en un flujo también, y entonces el flujo ya no manda su siguiente paso.
- **En una línea con bot, el flujo contesta enseguida.** No espera los «N minutos en chats nuevos» del bot. Sí obedece
  sus paradas de persona: **no empieza mientras una persona tiene o pausó el chat**, ni con la línea en pausa, ni con un
  traspaso pendiente, ni con el bot en pausa o apagado. En una línea con turno `off_hours` el flujo corre **solo fuera
  del horario**: dentro del horario contesta la gente.
- **Quien no califica (`end`).** Con `agent_quiet_days`, el agente o el bot se queda callado con esa persona esos días.
  Un agente de Frepi no le escribe. Un bot no recibe ese chat en su lista de pendientes (sale contado como
  `waiting_on.flow_quiet`, con la fecha en que vuelve) y `chatbot_get_chat` lo dice; si el bot contesta de todos
  modos, Frepi no se lo impide. Sin el valor, el chat vuelve normal al próximo mensaje.
- **El bot ve el estado de cada chat.** `chatbot_unread_chats` es «tus chats asignados»: los chats en que es el turno
  del bot y los que un flujo le pasó, con `flow_result`. Los que un flujo está atendiendo salen contados en
  `waiting_on.in_flow`; con `view: "in_flow"` se listan (skill `atender-chats`).
- **Cada mensaje del flujo se identifica** como «Flujo chatbot» (tipo de remitente `chatbot_flow`, con el nombre y la
  versión del flujo); en Conversaciones sale con el nombre del flujo.

## 7. Quién activa: una persona

Activar es siempre un acto de una persona. Por MCP, un cambio solo puede **guardar o apretar**: agregar un flujo
apagado, guardar una versión (queda pendiente), pausar, quitar, sacar una línea, y cambiar las líneas o el orden de un
flujo que **no** está vivo. Lo que **amplía** lo hace una persona: activar una versión, reanudar, sumar una línea a un
flujo vivo, reordenar flujos vivos.

- **(A) en el copiloto**, la tarjeta de `bi_agent_config_apply` es la persona: una versión nueva se activa al
  confirmar, y la tarjeta lista los pasos que cambian, los textos que ve el cliente (los enlaces completos), la
  situación, el `to` y las líneas. **Por MCP** el cambio de un flujo vivo se guarda pendiente, y lo que amplía se
  rechaza con `agent_doc_person_required` (con las rutas en `details.paths`): dile a la persona que lo active en la
  página del agente (Líneas y canales) o en el copiloto.
- **(B) en un bot**, `chatbot_setup` nunca es una persona: activar, reanudar, sumar una línea a un flujo vivo o
  reordenar responde `FLOW_NEEDS_PERSON` con `details.activate_url`; dale ese enlace al dueño, que lo activa en
  Conectar IA → Conexiones → el bot. El dueño necesita **Leer y enviar** y poder escribir en la línea
  (`FLOW_NEEDS_READ_SEND`).
- Pausar o quitar un flujo anterior deja que el siguiente tome a quienes tomaba. Es lo esperado.

## 8. Cambios después

- Un flujo vivo o en pausa se cambia con su documento: léelo (`bi_agent_config_get`, o `chatbot_settings` con
  `flow_documents=true`), edita ese documento y guárdalo; queda una versión pendiente y una persona la activa.
- Cambiar etiquetas, pausar y quitar se hacen también en la página del agente o del bot. Pausar no cambia el flujo; al
  reanudar vuelve la misma versión.
- Si el agente recibe a alguien mucho después de su último mensaje (más de 30 minutos), no se le despierta: contesta el
  próximo mensaje de esa persona.
- Cómo va: `bi_agent_run_rates(group_by="flow_version")` da el embudo (entraron, cada paso, a dónde llegaron) y el
  costo por lead (solo el flujo, y el flujo más el agente). `bi_agent_runs_find` lista las corridas de un flujo, también
  las de un bot (`agent_kind="chat_filter"`).

## 9. Errores

| Código | Qué hacer |
| --- | --- |
| `agent_doc_flow_invalid` | (A) Corrige cada ruta de `details.problems` (`code`, `path` como `flows[0].document…`, `message`) y reenvía. Nada se guardó. |
| `FLOW_INVALID` | (B) Lo mismo con las rutas `flows[i]…`; un `flow_id` que no es de este bot sale como `flows[i].flow_id`. |
| `agent_doc_flows_off` · `FEATURE_DISABLED` | Los flujos no están activos en este workspace (o falta `line_agents` / `chat_filters`): dilo. |
| `agent_doc_person_required` | (A) Aplica el resto sin esas rutas; una persona activa o amplía (sección 7). |
| `FLOW_NEEDS_PERSON` | (B) Dale `details.activate_url` al dueño; lo activa él en la página del bot. |
| `FLOW_NEEDS_READ_SEND` | El dueño del bot no puede enviar (nivel Leer, perdió la línea o su rol): arréglalo antes de activar. |
| `flow_line_taken` · `FLOW_LINE_TAKEN` | Otro dueño ya tiene flujos vivos en esa línea: usa otra línea o pausa los de él. |
| `FLOW_PREVIEW_STALE` | Cambiaste el documento o los casos después de ver la tarjeta: pide una tarjeta nueva. |
| `FILTER_STATE_UNAVAILABLE` | Los ajustes del bot se guardaron y los flujos no: vuelve a llamar con los mismos `flows`. |
| `flow_owner_mismatch` | Ese flujo no es de este agente o bot, no existe o se quitó: vuelve a leer la lista. |
| `handoff_target_invalid` | Un agente solo pasa a `agent` o `team`; un bot, a `bot` o `team`. |
| `handoff_agent_on_timeout` | Un traspaso al agente o al bot no puede llegar solo por tiempo: pon un `end` en `on_timeout`. |
| `answered_hours_invalid` | `skip_if_answered_hours` va de 1 a 720. |
| `flow_lines_invalid` | Una línea del flujo no es del dueño (o no contesta él ahí). |
| `too_many_flows` | Hasta 10 flujos por agente o bot: quita uno. |
| `flow_case_phone` | Un caso de prueba trae un teléfono: quítalo. |
| `tag_unknown` · `form_unknown` · `form_unavailable` | Usa una etiqueta o un formulario que existan (y que esté publicado en la línea). |
| `ai_pick_needs_free_text` | Un `ai_pick` solo se alcanza por `on_other` de una pregunta o un formulario. |
| `CELL_NOT_READY` | Avísale al soporte de Frepi. |
