# Guía para crear un agente de Superchat

Esta guía es para un asistente de IA (Claude u otro). Con ella y el contexto de un negocio vas a generar un **paquete de agente**: un archivo JSON con `"format": "superchat.agent"` y `"version": 3` que se importa en **Superchat → Panel de Control → Comportamiento → Importar agente** (o se aplica con la API de configuración, `PUT /v1/config`).

El paquete define cómo atiende una línea de WhatsApp: quién es la IA y qué sabe, qué datos se guardan de cada contacto, los valores del negocio (precios, fechas, enlaces) y los **flujos**: los mensajes automáticos con botones, los pedidos de datos, los recordatorios, las esperas y las condiciones.

## Tu tarea

1. **Lee el contexto del negocio** que te den.
2. **Pregunta lo que falte**, en un solo mensaje. No inventes datos: un precio, un horario o un enlace inventado termina en una respuesta falsa a un cliente real.
3. **Diseña** qué resuelven los flujos y qué la IA (ver «Cómo diseñar»).
4. **Genera el paquete**: un solo bloque JSON válido, sin comentarios ni texto dentro del bloque.
5. **Revisa la lista de verificación** del final antes de entregarlo.

Datos que necesitas (pregunta los que falten):

- Nombre del negocio, giro y ciudad; nombre y estilo del agente.
- Productos o servicios: precios, qué incluyen y, si aplica, fechas de inicio, cupo, promociones con su vigencia y enlaces (pago, agenda, mapa).
- Horario de atención, ubicación, formas de pago y facturación.
- Políticas: citas, cancelaciones, envíos, garantías, reembolsos, lo que aplique.
- Qué cuenta como éxito (agendar, vender, inscribir, calificar) y qué datos del contacto hacen falta para lograrlo.
- Cuándo pasar la conversación a una persona y qué temas no debe tratar la IA.
- Las preguntas que hacen siempre los clientes.
- Cómo llegan los contactos: anuncios de clic a WhatsApp (con qué texto prellenado), escriben directo o ambos.
- Qué conversiones quieren medir en Meta.
- Si quieren recordatorios cuando el contacto no contesta, y cada cuánto.

## Cómo trabaja Superchat

### Flujos e IA

Un **flujo** es un diagrama de **nodos** conectados. Cada nodo hace una sola cosa:

| Nodo | Qué hace |
|---|---|
| **Mensaje** (`message`) | Envía uno o varios contenidos (texto, imagen, video, audio, documento). Con botones (1 a 3), cada botón es una salida. |
| **Acción** (`action`) | Ejecuta acciones: poner o quitar una etiqueta, guardar un dato, enviar un evento de Meta, llamar un webhook. |
| **Condición** (`condition`) | Elige una salida según reglas: fecha, etiqueta, dato del contacto, nodo visitado o cita. |
| **Esperar** (`wait`) | Escucha durante un tiempo; opcionalmente, hasta obtener datos del contacto o una etiqueta. |
| **Ir a** (`goto`) | Continúa en otro flujo. |
| **Componente** (`call`) | Usa un componente: un flujo reutilizable que recibe parámetros y regresa con un resultado. |
| **Agendar cita** (`book`) | Ofrece los horarios libres de la agenda de citas y aparta el que elige el contacto. |
| **Terminar** (`end`) | Termina el flujo: conversación libre con la IA, pasar a una persona, sin más o regresar de un componente. |

Un flujo hace siempre lo mismo, así que es lo indicado para lo que tiene que salir bien siempre: la bienvenida, la presentación de un producto, la toma de datos, un recordatorio, pasar el chat a una persona.

La **IA** tiene dos papeles acotados:

- El **intérprete** lee lo que escribe el contacto y lo traduce a algo que el sistema entiende: qué botón quiso elegir, qué datos dio, qué intención expresó, si hizo una pregunta y qué preguntas frecuentes y temas sirven para contestarla. El código valida cada valor (que el botón exista, que el correo sea un correo, que el valor esté en la lista).
- El **respondedor** redacta lo que se le dice al contacto: las respuestas a sus preguntas, la conversación libre y los seguimientos.

Además, cada línea puede encender **tareas especiales** (`behavior.specialTasks`): trabajos puntuales de la IA que corren por su cuenta, con un prompt corto hecho para esa tarea. Solo guardan datos: no envían mensajes ni mueven los flujos (ver [Tareas especiales](#tareas-especiales-specialtasks)). En la misma lista se enciende `appointments` («Agendar citas»), que no es una tarea de la IA: activa la [agenda de citas](#calendar-agenda-de-citas), con su nodo «Agendar cita».

La IA **no cambia nada por su cuenta**: no pone etiquetas, no inicia flujos, no pasa el chat a una persona y solo guarda los datos que el intérprete reconoce y el código valida. Lo demás lo hacen los nodos de los flujos y las **intenciones** que defines en el paquete.

### Lo que hace el contacto

El contacto solo hace dos cosas: **toca un botón o escribe**.

- **Un botón** sigue su salida, sin pasar por la IA.
- **Lo que escribe** lo lee el intérprete. Si trae lo que el flujo espera (un dato, o la elección de un botón dicha con sus palabras), se guarda y el flujo sigue; si no, le contesta la IA y lo que esperaba el flujo sigue esperando.

Con la respuesta automática encendida (`respondIncoming`, pestaña Intervalos) **siempre hay respuesta**, a cualquier hora: cada mensaje del contacto recibe la del flujo (el nodo que sigue) o la de la IA, aunque sea un «ok», un emoji, una reacción o un sticker (a esos, la IA contesta con algo muy breve). Con la respuesta automática apagada, la IA no escribe: los flujos siguen funcionando y el intérprete solo lee lo que escribe el contacto cuando el flujo espera algo (botones vigentes o datos).

### Quién atiende cada mensaje

Con el chat en modo Bot, cada mensaje del contacto se revisa en este orden y se queda en el primer punto que lo atiende:

1. **Botón de la agenda de citas** («Cambiar cita», «Cancelar cita», «Sí, cancelarla», «No, conservarla» y, si ya tenía cita y eligió otro horario, «Sí, cambiarla»): ver [Agenda de citas](#calendar-agenda-de-citas).
2. **Botón de un flujo.** Sigue la salida de ese botón, sea del mensaje vigente o de uno anterior: los botones no caducan, así que un botón de un mensaje de hace días empieza desde su salida, aunque el contacto esté en otro flujo (qué pasa con el flujo en que estaba: ver [Flujo suspendido](#flujo-suspendido)). El texto del botón debe seguir siendo el mismo (un título con variables se reconoce ya con sus valores): si el botón ya no existe en su flujo o cambió de texto, el mensaje se trata como texto. Lo mismo con un botón de un flujo desactivado, si el contacto ya no está en ese flujo. Un horario de «Agendar cita» se intenta apartar. Dos toques al mismo botón en 10 segundos cuentan como uno (los de la agenda de citas, también con el chat en Persona).
3. **Texto igual a un botón vigente** (sin importar mayúsculas ni acentos): cuenta como tocarlo. Lo mismo con el texto de un horario ofrecido que no ha pasado y con el de los botones de una pregunta sobre su cita mientras está vigente («Sí, cancelarla», «No, conservarla», «Sí, cambiarla»).
4. **Palabra clave.** Si el texto contiene la palabra clave de un flujo activo, como palabra completa y sin importar mayúsculas, acentos ni signos, entra a ese flujo y deja el que tuviera. El mismo flujo no se relanza en un chat antes de 1 minuto ni más de 3 veces por hora: en esos casos el mensaje sigue al punto 6, salvo que sea solo la palabra clave repetida en ese minuto (el doble toque del anuncio), que se ignora.
5. **Contacto nuevo.** Si es el primer mensaje del contacto y la línea tiene un flujo de bienvenida activo, entra ahí.
6. **Por interpretar.** El texto (o la descripción de un archivo) se junta con los que lleguen enseguida y se interpreta cuando pasa el tiempo de recepción de la línea sin mensajes nuevos, con un tope de 60 segundos desde el primero y 10 mensajes.

El mensaje que activa un botón, una palabra clave o la bienvenida no pasa por el intérprete: el flujo es la respuesta. Si el contacto había escrito algo justo antes y estaba por interpretarse, eso solo se interpreta para guardar sus datos y, si preguntó algo, la IA lo contesta (si ahí pide algo urgente, también se atiende: ver [Al interpretar](#al-interpretar)). Es el mismo orden siempre, también cuando alguien del equipo inicia un flujo desde el chat.

Una palabra clave y la bienvenida son **inicios explícitos**: reemplazan al flujo en curso y descartan el flujo suspendido, si había uno (ver [Flujo suspendido](#flujo-suspendido)). Lo mismo cuando alguien del equipo inicia o detiene un flujo desde el chat. Pedir agendar o cambiar la cita, **por escrito o con el botón «Cambiar cita»**, va como una intención con destino: con un flujo en curso, lo deja suspendido; sin flujo en curso, entra al flujo para agendar y el suspendido, si hay, se conserva. Un botón o un horario de un mensaje anterior depende del flujo del que es (ver [Flujo suspendido](#flujo-suspendido)).

Con las tareas especiales `inferName` o `inferOrigin` encendidas, los primeros mensajes de un contacto nuevo se juntan, corren las tareas (las dos a la vez) y después se atienden en este orden, mensaje por mensaje. Si uno de ellos trae una palabra clave, entra a ese flujo (no a la bienvenida). En cuanto arranca un flujo (por la palabra clave, un botón o la bienvenida, o porque el equipo lo inició durante esa espera), los demás mensajes solo se interpretan para guardar datos y contestar si preguntaron algo: el contacto no recibe dos saludos. Si alguien del equipo escribe durante esa espera, se guardan los datos de las tareas, pero el bot no contesta.

### Al interpretar

El intérprete lee lo que escribió el contacto una sola vez. Primero se guardan los **datos que se guardan siempre** (los de captura `always`, el nombre y, con `inferOrigin`, la procedencia si la menciona), siempre que sean válidos. Después, el sistema decide en este orden y se queda en el primer punto que lo atiende:

1. **Urgente.** Una intención con la acción «Pasar a una persona» (`handoff`) va antes que todo, aunque el mensaje también elija un botón o dé un dato, y también en lo que el contacto escribió junto con un botón o una palabra clave. Los datos que espera el Esperar se guardan si los dio, se ejecutan sus acciones, la IA contesta con su instrucción (`reply`) y el chat pasa a una persona. El flujo **queda en pausa, no termina**: al devolver el chat al bot sigue donde iba, igual que cuando el equipo pasa el chat a Persona (ver [Cuando atiende una persona](#cuando-atiende-una-persona)). Un Terminar `human` en un flujo sí termina el flujo, porque es explícito.
2. **Lo que espera el flujo.** Si el flujo está en un Esperar de datos, se guardan los que dio y sean válidos (también los de captura `asked`), y una imagen o un documento llena el dato de tipo Archivo que se espera; con todos los datos, el Esperar sale por «Lo obtuvo». Si eligió con sus palabras uno de los botones vigentes («el primero», «la de ortodoncia»), sigue la salida de ese botón. Si contesta con sus palabras una pregunta sobre su cita («sí, cancélala», «mejor no»), cuenta como el botón: «¿La cancelamos?» durante 2 horas después de preguntarla y «¿La cambiamos…?» mientras esté vigente. La pregunta solo vale si fue lo último que preguntó el bot: en cuanto sale otro menú u otra oferta de horarios, deja de valer (así un «sí» que contesta ese menú no cancela ni cambia la cita). Contestarla con sus palabras funciona también con la respuesta automática apagada (el intérprete lee lo que escribe porque hay una pregunta pendiente), igual que escribir el título exacto del botón («Sí, cancelarla»). En «Agendar cita», un horario ofrecido se aparta, otro día u hora («el jueves en la tarde») ofrece horarios de ese momento y no querer agendar sale por `declined`.
3. **Caminos del flujo.** El intérprete ve lo que diría el flujo desde donde está escuchando (ver [Caminos del flujo](#caminos-del-flujo)). En una pausa, si lo que sigue contesta lo que pregunta o pide el contacto, el flujo **se adelanta**: lo que seguía sale ya, sin esperar el tiempo, y la IA no lo contesta aparte. En un menú, si el contacto no eligió ningún botón pero pide justo lo que da uno, el flujo sigue ese botón **solo si su camino únicamente informa** (no pone etiquetas ni datos, no envía eventos ni webhooks, no agenda y no pasa a una persona, tampoco después de una pausa).
4. **Intención.** Todas las intenciones aplican, también a media conversación. Se ejecutan sus acciones y el chat entra a su destino (`target`): el flujo en que estaba queda **suspendido** y sigue donde iba cuando termine el de la intención (ver [Flujo suspendido](#flujo-suspendido)). Si el destino está en el flujo en que ya está el contacto (su inicio o un nodo suyo), no lo mueve: la IA contesta y el flujo sigue donde va. Sin destino (o si el destino es un flujo desactivado), la IA contesta siguiendo su instrucción (`reply`) y lo que esperaba el flujo sigue esperando. Con la agenda encendida, si pide agendar o cambiar su cita entra al flujo para agendar (`calendar.flow`), como cualquier intención con destino: con un flujo en curso, ese flujo queda suspendido y sigue al terminar la agenda. Si dice un día u hora («quiero agendar el jueves en la tarde»), los horarios son de ese momento. Si pide cancelarla se le pregunta antes con botones, en cualquier momento.
5. **Nada lo cubre.** Contesta la IA y lo que esperaba el flujo sigue esperando.

Si se resolvió en los puntos 2 a 4 y además preguntó algo que eso no contesta, la IA contesta solo eso, antes del mensaje del flujo: si pregunta dos cosas, el flujo avanza con una y la IA contesta la otra. Si se resolvió sin pregunta, la respuesta es lo que envía el flujo; si el flujo no envía nada, contesta la IA. Sin flujo en curso aplican los puntos 1, 4 y 5 (más la agenda).

Un dato que no es válido (un correo incompleto, una opción que no está en la lista) no se guarda, y la IA se lo pide de nuevo con sus palabras. Si alguien del equipo escribió mientras se juntaban los mensajes, se aplican los puntos 1 a 4, pero la IA no contesta: ya contestó el equipo.

### Caminos del flujo

Para que la IA no conteste lo que el flujo va a decir (y el flujo no lo repita después), el intérprete recibe una **vista previa** de lo que diría cada camino desde donde escucha el flujo, hasta que vuelve a escuchar (un Esperar, botones, horarios) o termina. La vista previa no ejecuta nada: las Condiciones se evalúan con los datos de ese momento, las Acciones se saltan, un Esperar que ya tiene su dato sigue por «Lo obtuvo», entra y sale de los componentes y sigue los «Ir a». Llega hasta 300 caracteres y 30 nodos; los botones aparecen como «[botones: A, B]» y un Agendar cita como «[ofrece horarios para agendar una cita]» (con la agenda apagada, sigue por «Sin horarios», como al ejecutarlo; si «Sin horarios» no está conectado, la vista previa termina ahí sin texto, aunque al ejecutarlo se envía `texts.noSlots`).

| Dónde escucha el flujo | Qué caminos ve el intérprete |
|---|---|
| **Pausa** (un Esperar solo de tiempo, sin `for`) | Uno: su «Siguiente». Si eso contesta lo que pregunta o pide el contacto, el flujo se adelanta: lo que seguía sale ya y la IA no contesta eso aparte. En la bitácora: «Esperar: lo que sigue contesta lo que pidió el contacto, así que se adelanta». |
| **Menú** (un Mensaje con botones, también mientras corren sus recordatorios) | Uno por botón: su título, lo que dirá y la marca `[informa]` si el botón tiene «La IA puede elegirlo» y su camino solo informa (no pasa por un nodo Acción con acciones, por un «Agendar cita» ni por pasar a una persona). Lo que dirá se corta en el primer Esperar, pero para saber si solo informa el camino se sigue también después de un Esperar que vence por tiempo, porque lo que corre al vencer no lo decide el contacto: un camino con Acciones, agenda o pase a una persona después de una pausa no solo informa. Tampoco uno que pasa del tope de 30 nodos. Si el contacto **eligió** un botón, aunque sea con otras palabras, el flujo lo sigue (`option`). Si no eligió ninguno pero **pide justo lo que da** un botón `[informa]`, el intérprete lo devuelve en `wants` y el flujo lo sigue solo si ese camino de verdad únicamente informa; si no, queda en la bitácora y el menú sigue esperando a que el contacto lo elija. Así, un botón que confirma, paga, pone una etiqueta o agenda solo lo elige el contacto, también si eso pasa después de una pausa. Lo que sigue a los recordatorios del menú no se adelanta. |
| **Esperar de datos o de etiqueta** | Ninguno: su salida necesita lo que espera. La IA contesta y el flujo sigue esperando. |

Las intenciones con destino también: el intérprete ve lo que dirá el flujo de cada una, en 120 caracteres («[lleva a un flujo que dirá: "…"]»), y las de «Pasar a una persona» van marcadas «[pasa el chat a una persona]». Así la IA no contesta lo que el flujo de la intención va a decir. Las que llevan al flujo en que ya está el contacto o al suspendido no llevan vista previa: no lo moverían, así que ese flujo no se enviaría.

Límites: la vista previa llega solo hasta el siguiente punto donde el flujo escucha (lo que está después de una segunda pausa no se ve, así que no puede adelantarse), y adelantar una pausa hacia un «Agendar cita» ofrece los próximos horarios, no los del día que pidió el contacto.

### Flujo suspendido

Una intención con destino ya no hace perder el flujo en que estaba el contacto: ese flujo queda **suspendido** y el contacto entra al flujo de la intención (desde su inicio o desde el nodo elegido). Pedir agendar o cambiar su cita con un flujo en curso funciona igual (ver abajo). Es el mismo mecanismo que la pausa de Persona. Cuando el flujo de la intención termina, el suspendido sigue donde iba, en silencio (no repite su último mensaje):

- si su Esperar ya tiene lo que buscaba (por ejemplo, el otro flujo puso la etiqueta), sale por «Lo obtuvo» o «Se cumplió»;
- si no, su tiempo vuelve a contar completo desde que regresa (no el tiempo que le quedaba);
- un menú sigue esperando su botón.

Reglas:

- **Un solo nivel.** Si el contacto está en el flujo de una intención (B, con A suspendido) y otra intención lo lleva a C, B termina y A sigue suspendido. Cuando C termina, vuelve A.
- **24 horas.** Si pasan más de 24 horas desde que se suspendió, ya no se retoma: se descarta en silencio, con una nota en la bitácora. Se revisa cada vez que pasa algo en el chat, así que desde entonces la cabecera ya no lo muestra.
- **Inicios explícitos.** Una palabra clave, la bienvenida y el equipo iniciando o deteniendo un flujo (también en Persona) reemplazan al flujo actual y descartan el suspendido. Una intención lo conserva, y pedir agendar o cambiar la cita también, por escrito o con el botón «Cambiar cita» (ver la regla de la agenda, abajo).
- **Un botón o un horario de un mensaje anterior** sigue desde su salida, según el flujo del que es:
  - del flujo en curso: el contacto se mueve dentro de ese flujo y el suspendido se conserva;
  - del flujo suspendido: ese flujo se retoma desde ese botón, con la palabra clave con que entró y la cita que estaba cambiando, y el actual termina;
  - de otro flujo, o de uno que ya terminó: empieza de nuevo y el suspendido se descarta.

  Siempre hay respuesta: lo que envía esa salida o, si no envía nada, la IA (que ve el botón en el historial).
- **Cómo termina el flujo de la intención.** Si termina normal (Terminar `free`, una salida sin conectar o un error), el suspendido sigue. Si termina con `human`, el suspendido espera a que devuelvan el chat al bot y entonces se retoma. Si termina con `silent` («Terminar sin más»), el suspendido se descarta. Si llega con un «Ir a» al flujo suspendido, el suspendido se descarta, porque el contacto ya volvió a él (su final no se repite). Lo que el contacto preguntó junto con el botón que terminó el flujo de la intención se contesta ya dentro del flujo retomado.
- **Una intención hacia un flujo en el que ya está.** Si lleva al flujo suspendido (a su inicio o a un nodo suyo), el flujo actual termina y el suspendido sigue donde iba, sin empezar de nuevo (si no sale nada, contesta la IA, que ya sabe en qué flujo está). Si lleva al flujo en curso, no lo mueve: corren sus acciones, contesta la IA y el flujo sigue donde va.
- **Lo que pedía el flujo suspendido.** Si su Esperar pedía datos y el contacto los da mientras está en el otro flujo, se guardan y, al retomarse, el Esperar sale por «Lo obtuvo». Un dato de tipo Archivo también: la imagen o el documento (un comprobante, por ejemplo) que manda mientras está en el otro flujo se guarda para el suspendido, salvo que el Esperar del flujo en curso también espere un archivo (entonces es para ese).
- **Cambios en los flujos.** Si el flujo, el nodo, el Esperar o el componente en que se quedó ya no existen, se descarta con un aviso en la bitácora.
- **La agenda, como cualquier intención con destino.** Pedir agendar o cambiar la cita, por escrito o con el botón «Cambiar cita», con un flujo en curso deja ese flujo suspendido y entra al flujo para agendar (`calendar.flow`); al terminar, el suspendido sigue donde iba. Sin flujo en curso, entra al flujo para agendar y el suspendido, si hay, se conserva. Si ya está en el flujo para agendar: con los horarios de su «Agendar cita» a la vista (vigentes o ya pasados) se le ofrecen de nuevo; si todavía no llega ahí, no se mueve y contesta la IA. Solo mientras está en el «Agendar cita» con horarios vigentes puede pedir únicamente cancelar; con una oferta que quedó de un flujo que terminó, o con horarios que ya pasaron, también puede pedir agendar. Pedir cancelar se atiende en cualquier momento. La IA solo le dice «en un momento te muestro los horarios» cuando de verdad salen justo después de su respuesta; si no, le pregunta si quiere verlos.

En el chat, la cabecera agrega ««Curso» sigue al terminar este» mientras el contacto está en el flujo de la intención. En la bitácora quedan «El flujo «Curso» queda suspendido en «espera»: sigue cuando termine «Ubicación»», «El flujo «Curso» sigue donde estaba («espera»)», «El flujo «Curso», que estaba suspendido, sigue desde un botón de un mensaje anterior», «Intención «temario»: el contacto ya está en «Curso», así que el flujo sigue donde va» y, si se descarta, «El flujo «Curso», que estaba suspendido, se descarta:» con el motivo.

### Botones vigentes

Un Mensaje con botones deja sus botones **vigentes**: el flujo los está esperando. Siguen vigentes aunque el flujo avance por su salida «Siguiente» (por ejemplo, a un Esperar y a los mensajes de los recordatorios), hasta que se envía otro Mensaje con botones, el flujo pasa a otro con «Ir a» o termina. Mientras están vigentes:

- el intérprete los conoce, con lo que dirá cada uno, así que el contacto puede elegir uno escribiendo («sí, quiero agendar») o, si el botón solo informa, pidiendo justo lo que ofrece (ver [Caminos del flujo](#caminos-del-flujo));
- la IA sabe qué se le preguntó y con qué botones, y no los repite como lista;
- las intenciones aplican igual que sin flujo: con destino en otro flujo, el menú queda suspendido y sigue esperando su botón cuando termine el flujo de la intención; con destino en el mismo flujo, el menú sigue esperando y contesta la IA.

### Toda espera tiene salida

Un flujo no se queda esperando para siempre sin que tú lo decidas:

- **Si se acaban los recordatorios de un menú** (vence el último Esperar y lo que sigue está sin conectar), el flujo termina: el chat queda en conversación libre, vuelven todas las intenciones y se programa el seguimiento automático. Los botones de ese último menú siguen sirviendo para la siguiente respuesta del contacto, tocados o escritos, y después se olvidan.
- **Un menú cuyo «Siguiente» no está conectado** sigue esperando su botón **sin límite de tiempo**, también si sale después de una pausa (el flujo solo termina «por tiempo» si después del menú venció un Esperar). Mientras tanto el chat está en el flujo y no hay seguimiento automático. La importación y el editor lo avisan («espera un botón sin límite de tiempo»). Conecta el «Siguiente» de cada menú a un Esperar, aunque sea sin recordatorio: así el menú también tiene salida.
- Dentro de un componente, una salida sin conectar regresa al flujo que lo usó.

Después de que el flujo termina, los botones de los mensajes anteriores siguen funcionando: tocar uno empieza desde su salida.

### Esperar: recordatorios, datos y etiquetas

El nodo **Esperar** es el único que escucha durante un tiempo. Puede esperar **nada** (una pausa), **datos** del contacto (uno o varios) o **una etiqueta**.

- **El tiempo** cuenta desde que se entró al nodo o desde el último mensaje del contacto, lo más reciente. Un mensaje del equipo no lo reinicia.
- **Si al llegar ya tiene lo que espera** (todos los datos con valor, o la etiqueta), sale enseguida por «Lo obtuvo» o «Se cumplió».
- **Mientras corre**, si el contacto toca un botón vigente, el Esperar se cancela y sigue la salida del botón; si escribe lo que se espera, se guarda y, con todos los datos, sale por «Lo obtuvo»; si escribe otra cosa, le contesta la IA y el Esperar sigue.
- **El equipo y la API** también cumplen un Esperar: al completar un dato en la columna del contacto o al poner la etiqueta, sale en ese momento. Una intención con la acción de poner esa etiqueta también lo cumple.
- **Al acabarse el tiempo** sigue por «Se acabó el tiempo» (o por «Siguiente», en una pausa). Lo que se envía entonces lo dispara el reloj: pasa por el horario de atención y por el tope de mensajes sin respuesta (ver [Puerta de envío](#puerta-de-envío)).

**Los recordatorios son nodos.** Un recordatorio es un Esperar seguido de un Mensaje: Mensaje con botones → Esperar 60 min → Mensaje del recordatorio → Esperar 5 h → Mensaje de despedida → Terminar. Los botones siguen vigentes durante toda la cadena. Si al final de la cadena no hay nada conectado, el flujo termina igual (ver [Toda espera tiene salida](#toda-espera-tiene-salida)). Ver [Patrones](#patrones).

**El Mensaje siempre se envía.** Si el contacto ya dio el dato que pregunta un Mensaje, el Mensaje sale igual y el Esperar que sigue sale enseguida por «Lo obtuvo», sin esperar respuesta. Por eso:

> **Recomendado: antes de preguntar un dato, pon siempre una Condición.** «Si {{correo}} tiene valor» → sigue sin preguntar; «Si no» → Mensaje «¿Cuál es tu correo?» → Esperar el correo. Así no se le vuelve a preguntar lo que ya dijo (en otro flujo, en una conversación anterior o en su primer mensaje).

> **Si el mismo dato se pide en varios flujos, conviértelo en un componente.** Por ejemplo, «Pedir correo»: Condición «{{correo}} tiene valor» → Terminar · Regresar (resultado `ya_lo_tenia`); «Si no» → Mensaje con la pregunta → Esperar el correo → «Lo obtuvo»: Regresar (`obtenido`), «Se acabó el tiempo»: Regresar (`sin_dato`). Cada flujo lo usa con un nodo Componente y sigue por el resultado. La Condición vive en un solo lugar.

Para el Esperar, un dato «ya se sabe» si lo dijo el contacto o lo puso el equipo o la API: el valor por defecto de un dato (`default`) y el nombre deducido de su perfil de WhatsApp no cuentan. Una Condición «tiene valor» sí los ve; para preguntar el nombre cuando solo salió del perfil, pon antes la regla «{{nombre_origen}} es igual a perfil» → preguntar. Un dato que el contacto escribe junto con la palabra clave o el botón que lo trajo («soy Pedro» + «RAP75») también libera el Esperar en el acto.

### Puerta de envío

Todo lo automático que sale (mensajes de los flujos, respuestas de la IA y seguimientos) pasa por las mismas reglas, en este orden:

1. **Modo Persona.** Con el chat en modo Persona no sale nada automático, salvo los avisos y recordatorios de la agenda de citas. Al pasar a Persona se cancela lo automático que estaba por salir, aunque fuera la segunda parte de un mensaje que estaba esperando su `delay` (ver [Cuando atiende una persona](#cuando-atiende-una-persona)).
2. **Ventana de 24 horas.** No sale nada automático si pasaron más de 24 horas (menos 5 minutos de margen) desde el último mensaje del contacto: WhatsApp no deja escribirle. Si un Mensaje del flujo ya no puede salir, el flujo termina con un aviso en la bitácora del chat; las acciones de antes sí corrieron.
3. **Horario de atención**, solo para lo que el bot envía por iniciativa propia (ver abajo).
4. **Tope de mensajes sin respuesta.** Lo que el bot envía por iniciativa propia (lo que sigue cuando vence un Esperar y el seguimiento automático) es un **mensaje proactivo**. Después de 4 seguidos sin respuesta del contacto (el número lo fija Superadmin), el siguiente no sale; si es de un flujo, el flujo termina con un aviso. Cualquier mensaje del contacto reinicia la cuenta.
5. **Retrasos.** Los `delay` entre contenidos se respetan siempre.

Además, si los flujos y la IA envían demasiados mensajes seguidos a un chat (40 en 10 minutos por defecto), el chat pasa a modo Persona.

### Horario de atención

Si la línea tiene horario (`scheduleEnabled`, pestaña Intervalos), el horario **solo detiene lo que el bot envía por iniciativa propia**: lo que sigue cuando vence un Esperar (un recordatorio, por ejemplo) y el seguimiento automático. Fuera de horario, eso se retoma al abrir; cada chat sale entre 0 y 5 minutos después de la apertura, para no enviar todos a la vez.

Todo lo demás se atiende a cualquier hora: la IA contesta lo que escribe el contacto, sus botones y palabras clave siguen su flujo, y lo que hace el equipo (iniciar un flujo, poner una etiqueta, completar un dato) se aplica en el momento.

Lo que dispara el reloj sale aunque sea fuera de horario en dos casos:

- **Conversación activa:** el contacto escribió en los últimos 15 minutos.
- **Esperar con `"anyHour": true`** («Seguir aunque sea fuera de horario»): sigue a su hora exacta.

**Nunca sale de madrugada.** Si a la hora de abrir ya habrían pasado 24 horas desde el último mensaje del contacto (WhatsApp ya no dejaría escribirle), lo retenido **no sale**: el flujo termina y queda un aviso en la bitácora del chat. Conviene que las cadenas de recordatorios sean cortas: si el primero se retuvo toda la noche, un segundo muy largo (de 19 horas, por ejemplo) caería después de esas 24 horas y no saldría.

Un Esperar corto justo después de que el contacto escribió no se detiene de noche, y uno de 2 horas que vence a las 23:00 se retoma al abrir.

### Al terminar un flujo

| Terminar | Qué pasa |
|---|---|
| `free` | Termina el flujo y la conversación queda libre: lo siguiente que escriba el contacto lo contesta la IA (si la respuesta automática está encendida). Con `"reply": true`, la IA escribe enseguida. Se programa el seguimiento automático. Si había un flujo suspendido, en lugar de eso sigue donde iba (ver [Flujo suspendido](#flujo-suspendido)). |
| `human` | Termina el flujo y pasa el chat a una persona: nada automático (flujos, IA ni seguimientos; solo los avisos de la agenda de citas) hasta que el equipo lo devuelva al bot. Si había un flujo suspendido, sigue entonces donde iba. |
| `silent` | Termina sin más mensajes y **sin** seguimiento automático: la IA no contesta el botón o el mensaje que llevó ahí, y el flujo suspendido, si había uno, ya no sigue. Si el contacto vuelve a escribir, lo atiende la IA como en `free`. |
| `return` | Solo en componentes: regresa al flujo que lo usó, por la salida de su `result`. |

Una salida sin conectar también termina el flujo, como un `free` sin `reply`, salvo en dos casos: con botones vigentes y sin que haya vencido un tiempo (un menú sin «Siguiente»), el flujo se queda esperando el botón, y dentro de un componente, regresa al flujo que lo usó (ver [Toda espera tiene salida](#toda-espera-tiene-salida)).

**Seguimiento automático.** Si la línea tiene encendidos el seguimiento (`respondLoop`) y la respuesta automática, cuando la conversación queda libre se programa un mensaje de la IA `loopMinAge` minutos después de la última actividad: al terminar un flujo con `free` (o por una salida sin conectar) y después de cada respuesta de la IA en la conversación libre. Un Terminar `silent` no lo programa. Es un mensaje proactivo: fuera de horario, sale al abrir.

### Cuando atiende una persona

El interruptor **Bot / Persona** del chat cambia quién atiende, no el flujo:

- **Al pasar el chat a Persona**, el flujo queda **en pausa** donde iba (la cabecera del chat muestra «flujo … en pausa»; «en pausa» se usa solo para Persona: un Esperar de tiempo se muestra como «esperando (pausa del flujo) hasta las…»): no corre ningún tiempo, se cancela lo automático que estaba por salir y lo que el contacto había escrito y estaba por interpretar ya no se interpreta (lo atiende la persona).
- **Al devolverlo al bot**, el flujo sigue donde estaba. Si estaba en un Esperar que ya tiene lo que buscaba (por ejemplo, la persona puso la etiqueta `pago-confirmado` mientras atendía), sale por «Se cumplió» o «Lo obtuvo». Si no, el tiempo del Esperar vuelve a contar completo desde ese momento. Un menú sigue esperando su botón.
- **Una intención con «Pasar a una persona»** (`handoff`) también deja el flujo en pausa: al devolver el chat al bot, sigue donde iba.
- **Sí terminan el flujo:** un Terminar `human`, pasarse del límite de cambios de una cita y el límite de mensajes automáticos del chat. En los tres casos, el flujo suspendido (si había) se conserva.
- **«Detener»** (el botón cuadrado de la cabecera del chat) termina el flujo en curso, aunque esté en pausa, y descarta el flujo suspendido. En Persona, aunque solo quede un flujo suspendido, el botón aparece y lo descarta. En Contactos, la acción «Detener el flujo» hace lo mismo con los contactos elegidos: cuenta los que están en un flujo y los que solo tienen uno suspendido, y los detiene y descarta a todos.
- **Un flujo suspendido** (ver [Flujo suspendido](#flujo-suspendido)) se conserva mientras atiende la persona y se retoma al devolver el chat al bot, cuando ya no hay otro flujo en curso.
- Iniciar un flujo desde el chat devuelve el chat al bot (y descarta el flujo suspendido).

### En el panel

- **Sin leer.** El punto verde marca los chats con mensajes del contacto que nadie del equipo ha abierto. Es el mismo para todo el equipo y se quita cuando alguien tiene el chat abierto y a la vista (la pestaña visible y la ventana con el foco; si la ventana está detrás de otra, al volver a ella).
- **Sonido.** Suena un aviso por cada mensaje nuevo de un contacto, una sola vez aunque haya varias pestañas abiertas (también si llegan varios seguidos del mismo chat y cada pestaña los recibe agrupados de otra forma). Si el navegador lo silencia (porque todavía no se ha hecho clic en la página), aparece un aviso: haz clic en la página para activar el sonido.
- **Lo que envías** (un mensaje o un archivo) aparece al instante con un indicador que gira hasta que WhatsApp lo acepta (✓).
  - Si WhatsApp lo rechaza, o algo falla en el servidor antes de enviarlo (por ejemplo, al leer el token de la línea), el mensaje queda con ⚠ y puedes volver a escribirlo: ya no se queda girando para siempre.
  - Si WhatsApp ya lo aceptó, el mensaje queda como enviado aunque falle algo después (el problema queda en el registro de operaciones): así no lo reenvías y el contacto no lo recibe dos veces.
  - Si la llamada no llega a guardar el mensaje (por ejemplo, se cae la conexión), el texto vuelve a la caja si estaba vacía; si lo reenvías tal cual, lleva el mismo id. Si ya habías escrito otra cosa (o cambiaste de chat), no se juntan: el mensaje queda en la conversación con ⚠ y «No se envió», con **Reintentar** (con su mismo id) y **Descartar**. En los dos casos, si sí había salido, no sale dos veces.
  - Si el navegador no puede confirmar si salió, avisa: «No se pudo confirmar el envío…». Revisa el mensaje en el chat en unos segundos: si dice «No se envió», reenvíalo sin miedo (no le llegó al contacto); si dice «No se confirmó», lee el punto siguiente.
  - Si el servidor se cayó a medio envío, el mensaje no se queda girando para siempre: a los 2 minutos se ve con ⚠ y «No se confirmó el envío: puede que haya llegado», con **Reintentar**, que lo envía con su mismo id. **Antes de reintentarlo, revisa en WhatsApp si ya le llegó al contacto**: si el servidor se cayó justo después de que WhatsApp lo aceptó (o fallaron dos veces seguidas las escrituras que lo anotan), el mensaje sí salió y reintentarlo lo manda dos veces. La diferencia: «No se envió» quiere decir que no salió; «No se confirmó», que no se sabe.
  - Un archivo que no se pudo enviar se reintenta sin volver a subirlo; si lo descartas, se borra. Un nombre con varios puntos seguidos («Cotización...pdf») ya no hace que falle.
- **Bitácora.** Los cambios que haces a mano (etiquetas, datos, el nombre) quedan en la bitácora del chat con tu nombre de miembro o tu correo, también si los haces justo al abrir el panel.
- **Etiquetas.** Un chat tiene hasta 50. Si uno de antes tiene más, puedes quitarle etiquetas y renombrarlo, pero no agregarle otra hasta que tenga menos de 50.

### Protección anti-bucles

- Un evento envía como máximo 50 nodos con mensaje (Mensaje o Agendar cita) y recorre como máximo 200 nodos.
- Si un flujo vuelve a un nodo por el que ya pasó sin que el contacto haga nada, se detiene ahí. Volver a un menú con un botón sí funciona (cada botón empieza de nuevo), igual que lo que sigue a «Se acabó el tiempo»: un recordatorio puede volver al menú, y lo frena el tope de mensajes sin respuesta.
- Hasta 5 componentes anidados.
- Si los flujos y la IA envían demasiados mensajes seguidos a un chat (40 en 10 minutos por defecto), el chat pasa a modo Persona.
- Palabras clave: no se relanza el mismo flujo antes de 1 minuto ni más de 3 veces por hora.

### Qué recibe la IA

- **Tareas especiales:** solo lo que necesita cada una. `inferName` recibe el nombre del perfil de WhatsApp y los primeros mensajes del contacto; `inferOrigin`, el país de su teléfono y sus primeros mensajes.
- **Intérprete:** lo que espera el flujo y sus caminos (los botones vigentes con el texto del mensaje que los lleva, lo que dirá cada uno y la marca `[informa]` si su camino solo informa; en una pausa, lo que sigue; los datos que espera el Esperar, con su descripción, su tipo y el mensaje que los pidió; en «Agendar cita», los horarios ofrecidos y los próximos días con su fecha; la pregunta sobre su cita que está vigente, con sus botones; los datos que pedía el flujo suspendido; o nada), los datos que se capturan siempre (con su descripción y opciones), la procedencia (con `inferOrigin`), la cita del contacto y los tipos de cita (con la agenda encendida), todas las intenciones (id, descripción, ejemplos y, de las que moverían al contacto, lo que dirá el flujo de su destino en 120 caracteres, o si pasa el chat a una persona), las preguntas de las preguntas frecuentes, **solo los títulos** de los temas y los últimos 8 mensajes.
- **Respondedor:** `persona`, objetivos, reglas, **todo el catálogo**, solo las preguntas frecuentes y los temas que eligió el intérprete (hasta 3 de cada uno), los datos, la procedencia y las etiquetas del contacto, su cita y los tipos de cita (con la agenda encendida, junto con la regla de que no puede agendar, cambiar ni cancelar por su cuenta; con la agenda apagada, la cita que ya tiene y que las citas las gestiona el equipo), **la situación del flujo** (qué espera: un botón, los datos que faltan o la confirmación del equipo; o que no hay flujo), el dato que no fue válido, la instrucción de la intención si la hay y los últimos 40 mensajes. Los mensajes de los flujos, los botones que se mostraron y los que tocó el contacto están en el historial.

Si la organización agota su límite mensual de consultas a la IA, la IA deja de contestar (los flujos siguen funcionando).

Importar un paquete **no enciende** la respuesta automática ni el seguimiento: eso se activa aparte, en la pestaña Intervalos.

### Flujos activos y desactivados

Cada flujo está **activo** o **desactivado** (interruptor «Activo» en el editor, `"active"` en el paquete; los componentes siempre están activos). Un flujo desactivado sigue guardado y se puede editar, pero **no arranca solo**:

- sus palabras clave y su bienvenida no cuentan;
- una intención que lleva a él ejecuta sus acciones y contesta la IA, como si no tuviera destino;
- un «Ir a» que llega a él termina el flujo en que iba el contacto (queda un aviso en la bitácora);
- el Calendario no lo usa para agendar;
- un botón viejo de ese flujo se trata como texto si el contacto ya no está en él.

Alguien del equipo sí puede iniciarlo a mano desde el chat, para probarlo. Los chats que ya estaban dentro cuando se desactivó lo terminan normalmente. Al activarlo, se vuelve a revisar como al guardarlo: si algo dejó de ser válido mientras estaba desactivado (por ejemplo, un «Ir a» a un nodo que ya no existe), no se activa hasta corregirlo.

Sirve para preparar una versión nueva sin tocar la que atiende: en el editor, **Guardar como copia desactivada** guarda lo del lienzo como un flujo nuevo, desactivado, y deja el original como estaba. Como solo cuentan las palabras clave y la bienvenida de los flujos activos, las dos versiones pueden tener la misma palabra clave. Cuando la copia esté lista, desactiva la vieja y después activa la copia, en ese orden: dos flujos activos no pueden compartir una palabra clave ni la bienvenida. La lista de flujos marca los desactivados y tiene un filtro (Todos, Activos, Desactivados).

### Copias de respaldo

Antes de cada cambio que reemplaza o borra algo, Superchat guarda una copia de la configuración completa, de justo antes del cambio, con los flujos tal como estaban guardados: al importar un agente, borrar un flujo o restaurar una copia, con cada cambio por la API y al guardar desde el Panel un flujo que ya existía, el comportamiento o la agenda. Para no llenar la lista mientras editas, un guardado del Panel no hace otra copia si la más reciente ya es tuya, desde el Panel, de hace menos de 10 minutos: esa ya guarda lo que había antes de que empezaras. Activar o desactivar un flujo no hace copia. Una copia solo queda si el cambio se guardó (uno que da error no deja copia). Se conservan las últimas 10 y, además, la más reciente que guardó flujos de la v7, para poder recuperarlos.

Las copias se leen y se restauran con la API (`GET /v1/snapshots/{id}` y `POST /v1/snapshots/{id}/restore`). Restaurar una copia no se bloquea por lo que va entre flujos o hacia otras partes de la línea (una palabra clave repetida, una intención o un «Ir a» que lleva a un nodo que ya no existe, el Calendario, también un «Flujo para agendar» que no viene en la copia, un dato o un webhook que ya no existe): se aplica y esos problemas llegan como avisos para corregirlos después. Lo que está mal dentro de un flujo sí la bloquea. Si la copia se guardó cuando la línea todavía tenía flujos de la v7, esos flujos se restauran tal cual estaban, con un aviso: no se pierden, pero no funcionan hasta que los armes de nuevo con los nodos de la v8. Las copias guardadas con la v8.0 o la v8.1 no tienen esos flujos completos: si la línea tenía flujos de la v7, no se restauran (responden «El flujo no tiene nodos»).

### Quién cambia la configuración

Solo el **dueño** y los **administradores** de la organización editan la configuración de una línea: los flujos (también activarlos y desactivarlos), el comportamiento, los intervalos y la configuración del Calendario, e importan agentes. Todo se guarda en el servidor con la misma revisión que la importación, así lo que se guarda siempre se puede exportar y volver a importar, también al guardar el comportamiento (quitar un dato que espera un Esperar o un webhook que usa un nodo es un error) y al activar un flujo (se vuelve a revisar). La excepción es una línea que ya trae problemas que la revisión de hoy rechaza: después de restaurar una copia (que los deja pasar como avisos) o con datos guardados con la v8.0 (por ejemplo, dos palabras clave que solo difieren en signos o dos botones con el mismo texto). Hasta corregirlos, su exportación no se puede volver a importar. Si dos personas editan el mismo flujo, la segunda en guardar recibe un aviso en lugar de pisar los cambios de la primera, y puede guardar lo suyo como copia.

Los **monitores** ven todo eso en solo lectura y siguen con lo operativo: atender los chats, pasar un chat a Bot o a Persona, iniciar y detener flujos, poner etiquetas, completar datos y agendar, mover o cancelar citas.

## Formato del paquete

```text
{
  "format": "superchat.agent",   // siempre este valor
  "version": 3,                  // siempre 3
  "behavior": { … },             // comportamiento de la IA, datos, etiquetas, catálogo, intenciones
  "intervals": { … },            // tiempos y horario (opcional)
  "flows": [ … ],                // flujos y componentes ([] si no hay)
  "calendar": { … }              // agenda de citas (opcional: sin ella, la de la línea no cambia)
}
```

`exportedAt`, `appVersion` y `source` los agrega Superchat al exportar; no hace falta escribirlos. Usa solo los campos de esta guía:

- Una clave desconocida en el primer nivel del paquete es un error, con una sugerencia si parece una errata (`"journeys"` → «¿quisiste decir "flows"?»). `flows` es obligatoria (puede ir vacía, `[]`): sin ella, importar borraría todos los flujos de la línea.
- `version` es el número `3`, sin comillas.
- Dentro de los flujos, lo que la importación cambiaría en silencio da un aviso:
  - un tipo de nodo, un tipo de acción o un `mode` de Terminar que no existen;
  - una salida con un id que no es válido, también el `next` de un botón o de una regla y el `node` de un «Ir a»;
  - `results` o `params` que no son válidos, y un id de botón que no es válido;
  - la clave de un nodo que no es un id válido, o un nodo con un `id` distinto de su clave;
  - una clave que no existe en un nodo, en el flujo (con una pista: `keywords` → `entry.keywords`) o en `entry`;
  - un `once` que no es `true` ni `false` y un Esperar que espera una etiqueta y datos a la vez.

  Lo demás que no se reconoce (por ejemplo, una clave de más en un botón o en un contenido) se descarta.

Los paquetes de versiones anteriores de Superchat no se importan: sus flujos hay que armarlos de nuevo con los nodos de esta guía.

Importar reemplaza el comportamiento, los intervalos que traiga y **todos** los flujos (los de la línea que no vengan en el paquete se borran; los que siguen conservan su fecha de creación). Antes de reemplazar, Superchat guarda una copia de la configuración. Solo el dueño y los administradores de la organización pueden importar (ver [Quién cambia la configuración](#quién-cambia-la-configuración)).

### `behavior`

| Campo | Tipo | Qué escribir |
|---|---|---|
| `persona` | texto | Quién es el agente, para qué negocio trabaja, su tono y sus límites. Dos o tres párrafos breves. Hasta 20 000 caracteres. |
| `goals` | lista de textos | Objetivos en orden de prioridad: el primero es el principal. Hasta 50. |
| `rules` | lista de textos | Reglas de comunicación en orden de prioridad: la primera manda. Hasta 50. |
| `faqs` | lista | `{ "question", "answer" }`. Si la pregunta coincide, la IA usa esa respuesta. `answer` admite variables, como `{{catalogo.precio}}`. Hasta 500. |
| `knowledge` | lista | `{ "topic", "content" }`: información de referencia por tema. El intérprete ve solo `topic` para elegir qué temas pasarle al respondedor. Hasta 500. |
| `fields` | lista | Datos del contacto (ver abajo). Hasta 50. |
| `tags` | lista | `{ "name", "use" }`: etiquetas que ponen o revisan los flujos y las intenciones. `use` explica para qué sirve (lo lee el equipo). Usa minúsculas con guiones (`cita-solicitada`). Hasta 100. |
| `catalog` | lista | Valores del negocio (ver abajo). Hasta 200. |
| `intents` | lista | Intenciones (ver abajo). Hasta 100. |
| `webhooks` | lista | `{ "name", "url", "description" }` (ver abajo). Déjala en `[]` salvo que el negocio tenga un servicio propio que los reciba. Hasta 30. |
| `specialTasks` | objeto | Tareas especiales encendidas, como `{ "inferName": true, "inferOrigin": true, "appointments": true }` (ver abajo). Las que no vengan se conservan como estén en la línea, una por una. |

#### Datos del contacto (`fields`)

Cada dato se guarda por contacto y se usa como `{{nombre_del_dato}}`.

| Campo | Qué escribir |
|---|---|
| `name` | Minúsculas, números y guion bajo; empieza con letra; hasta 30 (`correo`, `horario_preferido`). |
| `label` | Nombre visible para el equipo. |
| `type` | `text`, `email`, `phone`, `number`, `date`, `choice` o `file` (Archivo). |
| `options` | Solo en `choice`: la lista de valores válidos (obligatoria). Se guarda la opción tal como está escrita aquí. |
| `description` | Qué dato es y en qué forma. El intérprete la lee para reconocerlo; en un `choice`, di qué frases corresponden a cada opción («brackets» es ortodoncia). |
| `capture` | `always`: se guarda en cuanto el contacto lo dice, en cualquier momento. `asked`: solo mientras un Esperar lo espera. |
| `default` | Valor que usan los textos mientras el contacto no tenga uno (puede ir vacío). |

Cómo se valida lo que dice el contacto: `email` se guarda en minúsculas si parece un correo; `phone`, solo los dígitos (de 7 a 15); `number`, el número sin `$` ni comas; `choice`, la opción de la lista que coincide (sin importar mayúsculas ni acentos), y el intérprete devuelve una opción cuando lo que dice corresponde claramente a ella según la descripción del dato; `text` y `date`, el texto tal cual (hasta 500 caracteres). Un valor que no pasa la validación no se guarda.

Un dato de tipo **Archivo** (`file`) guarda el enlace del archivo que envía el contacto (una foto, un comprobante, un PDF) mientras un Esperar lo espera. Solo lo llena una **imagen o un documento**: un sticker, una nota de voz o un video no cuentan (a eso le contesta la IA y el Esperar sigue). No lo llena el intérprete, así que la captura `always` no aplica: pídelo siempre con un Esperar.

El dato **`nombre` es del sistema**: no lo declares. Siempre existe, se captura siempre y guarda el nombre de pila en `{{nombre}}` y el completo en `{{nombre_completo}}`. Está vacío hasta que el contacto dice su nombre en la conversación, el equipo lo escribe o, con la tarea especial `inferName`, se deduce de sus primeros mensajes o de su perfil. `{{nombre_origen}}` dice de dónde salió. Si el contacto dice otro nombre después (se corrige: «no soy Pablo, soy Pedro»), el intérprete lo reemplaza. Un Esperar puede esperar `nombre` como cualquier otro dato. El nombre con el que aparece en la bandeja es `{{perfil}}`. Tampoco declares datos con el nombre de una variable del sistema (como `procedencia` o `cita`): la procedencia y la cita ya existen (ver [Variables en los textos](#variables-en-los-textos)).

#### Catálogo (`catalog`)

`{ "key", "value", "description" }`. Los valores del negocio que cambian con el tiempo: precios, fechas de inicio, cupos, enlaces, direcciones, teléfonos. `key` en minúsculas, números y guion bajo (hasta 40). Se usan en los textos como `{{catalogo.clave}}` y la IA recibe el catálogo completo en cada respuesta, así que un precio cambia en un solo lugar. `description` le dice a la IA qué es cada valor.

#### Intenciones (`intents`)

Lo que el contacto puede querer en cualquier momento de la conversación, y qué hacer cuando lo expresa.

| Campo | Qué escribir |
|---|---|
| `id` | Minúsculas, números, guion y guion bajo; hasta 40 (`agendar`, `no_interesa`). |
| `description` | Cuándo aplica, en una frase observable («Pregunta cuánto cuesta un tratamiento»). Obligatoria. |
| `examples` | Hasta 10 frases de ejemplo, variadas. |
| `target` | Destino: `"flujo"` (desde su inicio) o `"flujo:nodo"`. El chat entra ahí, salvo que ya esté en ese flujo (ver abajo). |
| `reply` | Instrucción para la respuesta de la IA cuando no hay destino. |
| `actions` | Acciones que se ejecutan al detectarla, antes del destino o de la respuesta (hasta 10; ver [Acciones](#acciones)). |

Cada intención necesita `target`, `reply` o `actions`. Usa `target` **o** `reply`, no los dos: con un destino activo, el flujo es la respuesta, la IA no escribe además y `reply` no se usa (la importación da un aviso); si el contacto además preguntó algo que ese flujo no dice, la IA lo contesta antes de entrar. El destino debe ser un flujo, no un componente, y el nodo de `"flujo:nodo"` debe existir. Las acciones deben usar eventos de Meta, webhooks y datos que existen; las etiquetas que pongan y no estén en `tags` se agregan con un aviso. Con la acción `handoff`, no entra al destino: va antes que todo, la IA contesta siguiendo `reply` y el chat pasa a una persona con el flujo en pausa. Por eso `handoff` va con `reply` y **sin** `target`: si trae los dos, la importación avisa que el chat no entra a `target`.

**Con un flujo en curso.** Todas las intenciones aplican también a media conversación (el chat espera un botón, un dato o una etiqueta, o está en una pausa), cada una en su lugar del orden (ver [Al interpretar](#al-interpretar)). Con destino, el flujo en que estaba el contacto queda suspendido y sigue donde iba cuando termine el de la intención (ver [Flujo suspendido](#flujo-suspendido)). Sin destino, el contacto sigue en su flujo: se ejecutan sus acciones, la IA contesta con su instrucción y lo que esperaba el flujo sigue esperando.

**Un destino en un flujo en el que el contacto ya está no lo mueve**, lleve al inicio o a un nodo: si es el flujo en curso, se ejecutan sus acciones, contesta la IA y el flujo sigue donde va (así no pierde lo que esperaba, por ejemplo la confirmación de un pago); si es el flujo suspendido, el flujo actual termina y el suspendido sigue donde iba. Para que una intención lleve a otra parte de un flujo largo, pon esa parte en un flujo aparte y apunta la intención ahí: entonces el flujo en curso queda suspendido.

El campo `interrupts` («Puede interrumpir un flujo», de versiones anteriores) ya no existe: no lo escribas. Si un paquete lo trae, se importa igual y se ignora, con un solo aviso para todas las intenciones que lo traen, por ejemplo «13 intenciones («temario», …, y 8 más) traen "interrupts", que ya no se usa y se ignora. Desde la v8.2 todas las intenciones aplican también con un flujo en curso: el flujo queda suspendido y sigue al terminar el de la intención.»

#### Webhooks (`webhooks`)

`{ "name", "url", "description" }`. `name` empieza con letra o guion bajo y usa letras, números y guion bajo; `url` empieza con `https://`. La acción `{ "type": "webhook", "name": "…" }` hace un `POST` a esa URL con el chat (`id`, `name`, `labels`, `data` con sus datos y `flow`, el flujo en que está), la línea y la hora.

#### Tareas especiales (`specialTasks`)

Un objeto con las tareas encendidas: `{ "inferName": true, "inferOrigin": true }`. Las tareas que el paquete no trae se conservan como estén en la línea, una por una. Si el paquete apaga `appointments` en una línea que agenda citas, la importación da el aviso de la agenda apagada (ya no se agendan citas nuevas y las citas próximas, con cuántas son, siguen con sus recordatorios): si la línea usa la agenda, no pongas `appointments` en el paquete. Cada tarea usa su propio prompt corto (no recibe `persona`, las preguntas frecuentes ni el historial), cuenta como una consulta a la IA y solo guarda datos. Las de primer contacto (`inferName` e `inferOrigin`) comparten la misma primera ráfaga y corren a la vez.

| Tarea | Qué hace |
|---|---|
| `inferName` | **Inferir nombre en nuevo contacto.** Cuando un contacto escribe por primera vez, sus primeros mensajes se juntan durante el tiempo de recepción de la línea (como máximo 15 segundos entre mensajes, 30 en total y 10 mensajes). Antes de atenderlos, la IA deduce su nombre: el que dice en ellos («Hola, soy Pedro») o, si no lo dice, el de su perfil de WhatsApp, limpio («JuANiT0» → Juan, «Laurita🌻» → Laura). Lo guarda en `{{nombre}}` y `{{nombre_completo}}` (este solo si hay apellidos), y en `{{nombre_origen}}` pone `conversacion` o `perfil`. El chat también toma ese nombre en la bandeja y el encabezado (`{{perfil}}`), salvo que el equipo le haya puesto otro; el del perfil queda en `{{nombre_original}}`. Si no hay un nombre confiable (el perfil es un negocio, «Mamá», solo emojis, o quien escribe dice ser otra persona), no guarda nada. Después, los mensajes se atienden en orden como siempre (bienvenida, palabra clave, intérprete). |
| `inferOrigin` | **Inferir procedencia.** Con la misma primera ráfaga, la IA lee si el contacto dice de dónde es o desde dónde escribe («soy de Monterrey» → Nuevo León, México); lo que diga tiene que aparecer en sus mensajes. Si no lo dice (o solo mandó archivos o botones), se deduce **de su teléfono sin consultar a la IA**: el país por el código de país y, en México, Estados Unidos y Canadá, el estado por la lada (dice dónde se dio de alta el número, no dónde vive; en una lada compartida entre estados solo se sabe el país). Lo guarda en `{{procedencia_pais}}` y `{{procedencia_estado}}` (`{{procedencia}}` = «Nuevo León, México») y en `{{procedencia_origen}}` pone `conversacion` o `telefono`. Si después dice otro lugar («ya vivo en Puebla»), el intérprete lo actualiza. No corre si el equipo ya la escribió. |
| `appointments` | **Agendar citas** (no consulta a la IA). Permite agendar citas nuevas: el nodo `book` («Agendar cita»), la intención de agendar o cambiar y agendar desde el Calendario. Apagada, solo se dejan de agendar citas nuevas: las que existen siguen con sus recordatorios y se pueden cancelar o mover. Ver [`calendar`](#calendar-agenda-de-citas). |

Con `inferName`, la bienvenida puede separar con una Condición los tres casos según de dónde salió el nombre:

```json
{
  "type": "condition",
  "rules": [
    { "when": { "kind": "field", "op": "empty", "field": "nombre" }, "next": "pedir_nombre" },
    { "when": { "kind": "field", "op": "equals", "field": "nombre_origen", "value": "perfil" }, "next": "saludo_perfil" }
  ],
  "else": "saludo"
}
```

- Sin nombre → un Mensaje que lo pide y un Esperar del dato `nombre`.
- Deducido del perfil → un saludo que no lo da por seguro: por ejemplo, un Mensaje «¿Hablo con {{nombre}}?» con los botones «Sí» y «No». Con «No», un Mensaje que pide el nombre y un Esperar de `nombre`: el nombre deducido del perfil no cuenta como dicho, así que el Esperar sí espera. Si el contacto lo corrige por escrito, el intérprete guarda el nombre que diga.
- Lo dijo en la conversación → saluda con `{{nombre}}` y sigue.

### `intervals`

Todos son opcionales; si no los pones, se conservan los de la línea.

| Campo | Rango | Significado |
|---|---|---|
| `timezone` | zona IANA | Por ejemplo `America/Mexico_City` o `America/Bogota`. Las fechas de las condiciones, el horario de atención y `{{fecha_hoy}}` usan esta zona. |
| `receptionTimeMin`, `receptionTimeMax` | 0–3600 s | Espera antes de interpretar lo que escribió el contacto. Cada mensaje nuevo la reinicia, así que una ráfaga se interpreta junta (con un tope de 60 segundos desde el primero). |
| `splitReplies` | true/false | Enviar las respuestas de la IA en varias partes, como una persona. |
| `splitMin`, `splitMax` | 1–4 | Número de partes. |
| `loopMinAge` | 1–1440 min | Minutos de silencio antes del seguimiento automático. |
| `scheduleEnabled`, `scheduleStart`, `scheduleEnd` | HH:MM | Horario de atención: fuera de él, lo que el bot envía por iniciativa propia (lo que sigue cuando vence un Esperar y el seguimiento) se retoma al abrir. La IA contesta a cualquier hora (ver [Horario de atención](#horario-de-atención)). |

La respuesta automática (`respondIncoming`) y el seguimiento (`respondLoop`) no van en el paquete: se encienden en la pestaña Intervalos o con `PUT /v1/intervals`.

### `calendar` (agenda de citas)

Con la tarea especial `appointments` encendida, la línea agenda citas. La configuración es esta sección (en la bandeja, **Calendario → Disponibilidad y Mensajes**); las citas se apartan con el nodo [`book`](#agendar-cita-book) o a mano desde el Calendario. Es opcional en el paquete: si no viene, la agenda de la línea no cambia. En ese caso, su «Flujo para agendar» tiene que venir en el paquete: si no viene, es un **error** (agrégalo, trae `calendar` con otro `flow` o cambia el flujo para agendar en el Calendario antes de importar), igual que si falta su nodo; al restaurar una copia, es solo un aviso. Si el flujo para agendar viene desactivado, la importación lo avisa. Los avisos de la agenda salen antes que los demás. Al exportar, solo viaja si tiene tipos de cita.

```json
{
  "types": [
    {
      "id": "valoracion",
      "name": "Valoración",
      "duration": 30,
      "buffer": 10,
      "capacity": 1,
      "notice": 120,
      "horizon": 21,
      "agenda": "",
      "location": "Av. Colón 123, Mérida",
      "description": "Primera consulta para revisar el caso y dar un presupuesto",
      "hours": {
        "mon": [{ "from": "09:00", "to": "14:00" }, { "from": "16:00", "to": "19:00" }],
        "tue": [{ "from": "09:00", "to": "14:00" }, { "from": "16:00", "to": "19:00" }],
        "wed": [{ "from": "09:00", "to": "14:00" }],
        "thu": [{ "from": "09:00", "to": "14:00" }, { "from": "16:00", "to": "19:00" }],
        "fri": [{ "from": "09:00", "to": "14:00" }],
        "sat": [{ "from": "10:00", "to": "13:00" }],
        "sun": []
      }
    }
  ],
  "special": [{ "date": "2026-12-25", "ranges": [], "note": "Navidad" }],
  "reminders": [
    { "before": 1440, "text": "Hola {{nombre}}, te recordamos tu cita de {{cita_tipo}} mañana, {{cita_fecha}}, a las {{cita_hora}}." },
    { "before": 120, "text": "Hola {{nombre}}, tu cita es hoy a las {{cita_hora}} en {{cita_lugar}}. ¡Te esperamos!" }
  ],
  "texts": { "cancelButton": "Cancelar cita" },
  "flow": "agendar",
  "changeLimit": 2
}
```

| Campo | Qué escribir |
|---|---|
| `types` | Tipos de cita (hasta 20). `id`: minúsculas, números, guion y guion bajo, hasta 30 (lo usa el nodo `book`). `duration`: minutos (5 a 720). `buffer`: minutos libres después de cada cita (0 a 240). `capacity`: citas de ese tipo que pueden coincidir en el mismo tiempo (1 a 100; más de 1 para clases o grupos). `notice`: anticipación mínima en minutos (0 a 43 200); un horario más cercano no se ofrece. `horizon`: hasta cuántos días hacia adelante se agenda (1 a 180). `agenda`: quién atiende o dónde (hasta 40 caracteres; vacío, la agenda general): los tipos con la misma agenda comparten horario (ver abajo). `location` y `description`: los conoce la IA y `location` es `{{cita_lugar}}`. `hours`: por día (`mon` … `sun`), franjas `{ "from": "HH:MM", "to": "HH:MM" }` (hasta 6, sin encimarse: dos franjas encimadas el mismo día son un error); un día sin franjas está cerrado. Los horarios empiezan al inicio de cada franja, uno tras otro (duración + margen), y terminan dentro de ella. |
| `special` | Días especiales para todos los tipos (hasta 100): `{ "date": "AAAA-MM-DD", "ranges": [], "note": "" }`. Sin franjas, ese día está cerrado; con franjas (sin encimarse), se usan en lugar de las de la semana. |
| `reminders` | Hasta 3: `{ "before": minutos (5 a 10 080), "text": "…" }`, con un `id` opcional (minúsculas y números, hasta 12; sin él se numeran solos: `r1`, `r2`…). Se envían antes de cada cita agendada, a cualquier hora (no esperan al horario de atención) y aunque el chat lo atienda una persona, **solo si el contacto escribió en las últimas 24 horas** (después WhatsApp no deja escribirle); si no, se anotan en su chat. Llevan los botones «Cambiar cita» y «Cancelar cita». Sin `reminders`, se usan dos: 24 h y 2 h antes. Si los cambias, valen también para las citas ya agendadas: se vuelven a programar al guardar. |
| `texts` | Cambia los mensajes del sistema de citas (solo los que escribas; los demás quedan por defecto). Ver la tabla de abajo. |
| `flow` | El **flujo para agendar**: `"flujo"` o `"flujo:nodo"`. A él entra el contacto cuando escribe que quiere agendar o cambiar su cita (con un flujo en curso, ese flujo queda suspendido y sigue al terminar la agenda) y cuando toca «Cambiar cita» (igual que pedirlo por escrito: con un flujo en curso, ese flujo queda suspendido). Debe tener un nodo `book` y estar activo. Sin él, «Cambiar cita» y pedir agendar o cambiar por escrito vuelven al nodo «Agendar cita» que agendó la cita que tiene; si no tiene ninguna, contesta la IA (que no puede agendar por su cuenta). |
| `changeLimit` | Cuántas veces puede el contacto cambiar su cita por su cuenta (0 a 10; 2 si no viene). Ver «Cambiar de horario». |

Si el paquete trae `calendar` sin tipos de cita, al importarlo se borran los tipos de la línea, y si trae otros tipos, avisa por cada tipo de la línea que se borra (las citas ya agendadas no cambian).

| `texts` | Por defecto | Cuándo se usa |
|---|---|---|
| `ask` | «Estos son los próximos horarios disponibles. ¿Cuál te acomoda? Si prefieres otro día u hora, escríbemelo.» | Texto de un nodo `book` nuevo en el editor. |
| `confirm` | «Listo, tu cita de {{cita_tipo}} quedó agendada para el {{cita_fecha}} a las {{cita_hora}}.» | Confirmación de un nodo `book` nuevo en el editor. |
| `changed` | «Listo, cambiamos tu cita de {{cita_tipo}} al {{cita_fecha}} a las {{cita_hora}}.» | Confirmación cuando el contacto cambia su cita de horario (en lugar del `confirm` del nodo). |
| `moveAsk` | «Ya tienes una cita de {{cita_tipo}} el {{cita_fecha}} a las {{cita_hora}}. ¿La cambiamos al {{cita_nueva}}?» | Ya tiene cita y eligió otro horario sin haber pedido cambiarla. `{{cita_nueva}}` es el horario que eligió. Va con «Sí, cambiarla» y «No, conservarla». |
| `options` | «Estos horarios están disponibles. ¿Cuál te acomoda?» | Pidió otro día u hora y hay horarios ahí. |
| `noMatch` | «No tengo horarios disponibles en ese momento. Estos son los más cercanos:» | Pidió un momento sin horarios. |
| `taken` | «Ese horario se acaba de ocupar. Estos siguen disponibles:» | Otro contacto apartó el horario primero. |
| `expired` | «Ese horario ya pasó. Estos son los próximos disponibles:» | Tocó el horario de una oferta anterior que ya pasó. |
| `noSlots` | «Por ahora no tengo horarios disponibles para agendar.» | No hay ningún horario y la salida `full` no está conectada. |
| `booked`, `moved`, `cancelled` | «Hola {{nombre}}, agendamos tu cita…», «…cambió al…», «…quedó cancelada.» | Avisos cuando el equipo agenda, mueve o cancela con «Avisar al contacto». |
| `cancelAsk`, `cancelDone`, `kept`, `gone` | «¿Confirmas que cancelamos tu cita…?», «Listo, cancelamos tu cita…», «Perfecto, tu cita sigue en pie…», «Esa cita ya no está vigente.» | Cancelar desde WhatsApp. `kept` también contesta «No, conservarla» y el horario que ya tenía. |
| `changeMax` | «Ya hiciste el máximo de cambios a tu cita por aquí. Te comunico con un asesor para ayudarte.» | Pidió cambiar su cita más veces de las permitidas (`changeLimit`); el chat pasa a un asesor. |
| `changeOff` | «Por ahora no se pueden cambiar citas por aquí. Escríbenos qué necesitas y un asesor te ayuda.» | Pidió cambiar su cita con «Agendar citas» apagado. |
| `changeButton`, `cancelButton` | «Cambiar cita», «Cancelar cita» | Botones de la confirmación, los avisos y los recordatorios (hasta 20 caracteres; vacío: sin ese botón). |

Cómo funciona con el contacto:

- **Una sola cita a la vez.** Un contacto tiene como máximo una cita próxima. El sistema no deja apartar una segunda en ningún caso, tampoco si el equipo y el bot agendan al mismo tiempo.
- **Preguntar antes de cambiar.** Si ya tiene una cita y elige otro horario sin haber pedido cambiarla, se le pregunta antes (`moveAsk`): «Sí, cambiarla» la mueve al horario nuevo y confirma con `changed`; «No, conservarla» contesta `kept` y sale por «No quiso agendar». Puede contestar con los botones, escribiendo su título o con sus palabras («sí, cámbiala»), mientras la pregunta esté vigente: deja de estarlo en cuanto sale otro menú u otra oferta de horarios, o termina el flujo. Cada pregunta lleva su horario: el «Sí» de una pregunta anterior no mueve la cita al horario de otra. Si llegó con «Cambiar cita» o dijo que quería cambiarla, se cambia directo. Si toca el horario que ya tenía, la cita sigue igual (no se repite «Agendada») y, dentro de un componente, regresa al flujo que lo usó.
- **Cambiar de horario tiene un límite** (`changeLimit`, 2 por defecto). Cuenta cada vez que el contacto cambia su cita de horario, o la cancela y vuelve a agendar antes de la hora que tenía. Al pasarse, recibe `changeMax` y el chat pasa a un asesor (la cita queda igual). La cuenta vuelve a cero cuando pasa la cita, cuando el equipo marca la asistencia o cuando el equipo la cancela. Lo que mueve el equipo no cuenta: el equipo siempre puede mover una cita.
- **Agenda compartida.** Los tipos de cita con la misma `agenda` (la misma persona o sala) comparten el tiempo: no se enciman citas de tipos distintos. Las de un mismo tipo coinciden hasta su `capacity` (por ejemplo, una clase de 8 lugares). El cupo se cuenta por traslape, incluido el margen entre citas, no solo por la hora de inicio. Si dos contactos eligen horarios encimados a la vez, solo uno se lo queda; al otro se le ofrecen los que siguen libres (`taken`).
- **Cambiar y cancelar.** «Cambiar cita» entra al flujo para agendar igual que pedirlo por escrito: con un flujo en curso, ese flujo queda suspendido y sigue al terminar la agenda; sin flujo en curso, el suspendido (si había uno) se conserva. «Cancelar cita» pregunta «¿Confirmas…?» con «Sí, cancelarla» y «No, conservarla», que también se contestan escribiendo o con sus palabras durante 2 horas, mientras sea lo último que preguntó el bot (otro menú u otra oferta de horarios la deja sin efecto). Contestar con sus palabras o escribir el título exacto del botón funciona también con la respuesta automática apagada. Los ids de botón `cita_si` y `cita_no` están reservados para estas preguntas: un flujo no puede usarlos. Lo mismo si pide cancelar por escrito, aunque esté viendo horarios. Pedir agendar o cambiar por escrito con un flujo en curso deja ese flujo suspendido y entra al flujo para agendar; al terminar, sigue donde iba. Pedir cancelar se atiende en cualquier momento. Con el chat en modo Persona funcionan los botones para cancelar; «Cambiar cita» lo atiende el equipo (el botón sigue en los recordatorios, pero tocarlo no hace nada). Si el equipo movió la cita después de preguntarle si la cancela, un «Sí, cancelarla» viejo vuelve a preguntar con la fecha nueva.
- **Apagar «Agendar citas»** (`appointments: false`) solo detiene las citas nuevas: las que existen siguen con sus recordatorios, el contacto las puede cancelar y el equipo moverlas, cancelarlas y marcar la asistencia. «Cambiar cita» responde `changeOff` y deja de aparecer en los recordatorios. La IA sigue viendo la cita que ya tiene el contacto y sabe que las citas las gestiona el equipo. Al apagarla (al guardar el Comportamiento o al importar un paquete que la apaga), el Panel da el aviso de la agenda apagada: ya no se agendan citas nuevas y las citas próximas, con cuántas son, siguen con sus recordatorios. El Calendario lo recuerda mientras está apagada.
- **La IA no agenda.** Conoce la cita del contacto y los tipos de cita, pero nunca dice que una cita quedó agendada, cambiada o cancelada ni ofrece horarios por su cuenta: eso lo hace el sistema.
- **El equipo** ve las citas por semana en el Calendario y en la columna del contacto: agenda, mueve, cancela (con o sin aviso al contacto, que solo sale con la ventana de 24 h abierta) y marca si asistió o no asistió (solo después de la hora de inicio).

### `flows`

Cada elemento es un flujo o un componente:

| Campo | Qué escribir |
|---|---|
| `id` | Letras, números, guion y guion bajo; hasta 60; único en el paquete. Sin `:` ni espacios. |
| `name` | Nombre visible. Obligatorio. |
| `kind` | `"flow"` (flujo) o `"component"` (componente). |
| `active` | Solo flujos, opcional: `true` (por defecto) o `false` (desactivado: no arranca solo; ver [Flujos activos y desactivados](#flujos-activos-y-desactivados)). |
| `description` | Para qué sirve (lo lee el equipo). |
| `entry` | Solo flujos: `{ "keywords": [], "welcome": false }`. `keywords`: palabras o frases que lo lanzan (cada una de 2 letras o más; una palabra clave no puede estar en dos flujos activos, sin importar mayúsculas, acentos ni signos: «Cotización» y «cotizacion» son la misma, e «INK-TALLER!» e «ink taller» también). Una palabra común («hola», «info», «precio», «curso», «cita», «pago»…) da un aviso: se activa a media frase. `welcome: true`: es el flujo de bienvenida de los contactos nuevos; **solo uno** activo en toda la línea. |
| `params`, `results` | Solo componentes: nombres de sus parámetros y de sus resultados (minúsculas, números y guion bajo; hasta 10 de cada uno). |
| `start` | Id del primer nodo (al que se conecta la Entrada). |
| `nodes` | Objeto con los nodos; la clave es el id del nodo (letras, números, guion y guion bajo; hasta 40). Hasta 300 nodos. |
| `layout` | Opcional: posiciones en el lienzo, `{ "id_del_nodo": [x, y] }`. Sin él, el editor acomoda los nodos solo. |

Un flujo sin palabras clave ni bienvenida solo se lanza desde una intención, desde otro flujo (nodo «Ir a») o a mano desde el chat. Lo que lleva a un flujo desde fuera se revisa con él: una intención o un «Ir a» que apunta a un nodo que ya no existe, un flujo convertido en componente al que llega una intención o un componente usado con «Ir a» son errores. Cada salida (`next`, `got`, `else`, el `next` de un botón o de una regla, los `results` de un componente…) apunta a un nodo **del mismo flujo** o es `null` (sin conectar); para pasar a otro flujo se usa un nodo `goto`.

El id de los botones que se envían a WhatsApp es `sc:<flujo>:<nodo>:<botón>`; el de un horario de «Agendar cita», `sc:<flujo>:<nodo>:h<minutos en base 36>`, y los de la agenda, `scc:<acción>:<id de la cita>` (los de «¿La cambiamos…?» llevan además el horario propuesto, con su inicio en milisegundos en base 36: `scc:move:<id de la cita>:<horario>`).

#### Cómo corre un flujo

El flujo avanza de nodo en nodo, sin esperar, hasta que llega a un punto donde escucha:

- **Mensaje sin botones:** se envía y sigue por `next`.
- **Mensaje con botones:** se envía y sus botones quedan vigentes. Si tiene `next`, el flujo sigue enseguida por ahí (normalmente a un Esperar con los recordatorios); si no, se detiene esperando un botón sin límite de tiempo (la importación lo avisa).
- **Acción:** ejecuta sus acciones y sigue.
- **Condición:** elige su salida y sigue.
- **Esperar:** el flujo se detiene a escuchar. Si ya tiene lo que espera, sale enseguida por `got`.
- **Agendar cita:** como un Mensaje con botones; los horarios ofrecidos quedan vigentes.
- **Salida sin conectar:** termina el flujo; dentro de un componente, regresa al flujo que lo usó. Si hay botones vigentes y no venció ningún tiempo (un menú sin `next`), se queda esperando el botón (ver [Toda espera tiene salida](#toda-espera-tiene-salida)).
- **Terminar:** termina como diga su `mode`.

#### Mensaje (`message`)

Envía uno o varios contenidos. Con botones, cada botón es una salida.

```json
{
  "type": "message",
  "say": [
    { "text": "Tenemos limpieza, blanqueamiento, ortodoncia y revisión general." },
    { "text": "¿Qué te interesa?", "delay": 3 }
  ],
  "buttons": [
    { "id": "limpieza", "title": "Limpieza dental", "next": "limpieza" },
    { "id": "ortodoncia", "title": "Ortodoncia", "next": "ortodoncia" },
    { "id": "otro", "title": "Otro tratamiento", "next": "otro" }
  ],
  "next": "espera_menu"
}
```

| Campo | Qué hace |
|---|---|
| `say` | De 1 a 5 contenidos (ver «Contenido»). |
| `buttons` | De 0 a 3 botones: `{ "id", "title", "next" }`. `id`: letras, números, guion y guion bajo, hasta 20, único en el nodo; `cita_si` y `cita_no` están reservados para las preguntas de la cita (usarlos es un error). `title`: hasta 20 caracteres, distinto de los otros botones del nodo (sin importar mayúsculas ni acentos: «Sí» y «si» son el mismo, y es un error); admite variables (sin contar las variables, el texto fijo cabe en 20; ya con sus valores, el título se corta a 20). `next`: el nodo al que lleva el botón (`null`: sin conectar). `aiPick` (opcional, `false` por defecto): `true` = «La IA puede elegirlo si el contacto pide lo que ofrece» (ver [Intenciones y caminos del flujo](#intenciones-y-caminos-del-flujo)); solo en un botón cuyo camino únicamente informa: si tiene un nodo Acción con acciones, «Agendar cita» o pasa a una persona (también después de un Esperar que vence por tiempo, en cualquier rama), es un error. |
| `next` | «Siguiente». Sin botones, el nodo que sigue. Con botones, el que sigue enseguida, mientras los botones quedan vigentes: un Esperar, con o sin recordatorios. `null` deja el flujo esperando un botón sin límite de tiempo, y la importación lo avisa. |

El Mensaje siempre se envía, aunque su «Siguiente» sea un Esperar de datos que ya los tiene (ver «El Mensaje siempre se envía»): para no volver a preguntar, pon antes una Condición.

#### Contenido (`say`)

`{ "text": "…", "media": null, "delay": 0 }`

- `text`: hasta 4096 caracteres; en el contenido que lleva los botones, hasta 1024 (límite de WhatsApp: más es un error de validación). Admite variables.
- `media`: `null` o `{ "type": "image" | "video" | "audio" | "document", "url": "…", "fileName": "…", "transcription": "…" }`. `url` es la que devuelve la API al subir el archivo (`POST /v1/media`) o la del editor. `transcription` no se envía al contacto: es lo que la IA sabe del archivo, así que escribe qué dice o muestra. En imagen, video y documento, `text` va como pie del archivo; en audio se envía aparte.
- `delay`: segundos (0 a 30) antes de enviar ese contenido. Sin `delay`, el primero sale de inmediato y los demás con una pausa breve. Los contenidos de un Mensaje salen siempre completos y en orden: el contacto no los puede pausar al escribir (ver [Varios contenidos en un Mensaje o Mensaje → Esperar](#varios-contenidos-en-un-mensaje-o-mensaje--esperar)).
- Los botones van con el **último** contenido: ese contenido necesita texto y no puede ser un audio.

#### Acción (`action`)

Ejecuta sus acciones en orden y sigue por `next`. Es el único nodo que pone etiquetas, guarda datos o envía eventos.

```json
{
  "type": "action",
  "actions": [
    { "type": "tag", "name": "interes-limpieza" },
    { "type": "set", "field": "tratamiento", "value": "limpieza" },
    { "type": "meta", "event": "ViewContent", "once": true }
  ],
  "next": "promo"
}
```

`actions`: hasta 20 (ver [Acciones](#acciones)). Cada acción corre cada vez que el flujo pasa por el nodo, salvo que tenga `"once": true` («Solo la primera vez en este chat»): entonces corre una sola vez por chat.

#### Condición (`condition`)

```json
{
  "type": "condition",
  "rules": [
    { "when": { "kind": "datetime", "op": "before", "datetime": "2026-12-01T00:00" }, "next": "con_promocion" },
    { "when": { "kind": "tag", "op": "has", "tag": "cliente" }, "next": "cliente" },
    { "when": { "kind": "field", "op": "equals", "field": "anuncio_id", "value": "120210000000000001" }, "next": "campana_verano" }
  ],
  "else": "sin_promocion"
}
```

Revisa las reglas en orden (hasta 10) y sigue por la primera que se cumple; si ninguna, por «Si no» (`else`). Las reglas están en [Condiciones](#condiciones).

#### Esperar (`wait`)

Una pausa, que sigue por «Siguiente» al acabarse el tiempo:

```json
{ "type": "wait", "minutes": 60, "next": "recordatorio_1" }
```

Datos del contacto, con «Lo obtuvo» y «Se acabó el tiempo»:

```json
{ "type": "wait", "minutes": 120, "for": { "fields": ["nombre", "correo"] }, "got": "gracias", "next": "sin_datos" }
```

Una etiqueta que pone el equipo, con «Se cumplió» y «Se acabó el tiempo»:

```json
{ "type": "wait", "minutes": 180, "for": { "tag": "anticipo-confirmado" }, "got": "confirmado", "next": "pregunta_pago", "anyHour": false }
```

| Campo | Qué hace |
|---|---|
| `minutes` | Minutos enteros, de 1 a 43 200 (30 días). `null` = sin límite, solo si espera algo (`for`). |
| `for` | `null` (una pausa), `{ "fields": [ … ] }` (de 1 a 10 datos de `fields` o `nombre`) o `{ "tag": "…" }` (una etiqueta de `tags`). |
| `got` | Solo con `for`: «Lo obtuvo» (datos) o «Se cumplió» (etiqueta). Sale por aquí en cuanto tiene todos los datos o la etiqueta, o enseguida si ya los tenía al llegar. |
| `next` | «Se acabó el tiempo» (con `for`) o «Siguiente» (en una pausa). |
| `anyHour` | `true` («Seguir aunque sea fuera de horario»): lo que sigue al acabarse el tiempo sale a su hora aunque la línea esté fuera de horario. Por defecto, se retoma al abrir. |

Cómo se comporta mientras espera: ver [Esperar: recordatorios, datos y etiquetas](#esperar-recordatorios-datos-y-etiquetas). Un Esperar de casi 24 horas o más que tiene algo en «Se acabó el tiempo» da un aviso: para entonces WhatsApp podría ya no dejar escribirle al contacto.

#### Ir a (`goto`)

```json
{ "type": "goto", "flow": "agendar", "node": null }
```

Continúa en otro flujo, en su inicio o en `node`. No puede ir a un componente. Si el flujo de destino está desactivado, el flujo termina ahí (queda un aviso en la bitácora del chat y la importación lo avisa).

#### Componente (`call`)

```json
{ "type": "call", "component": "elegir-horario", "params": { "tratamiento": "{{tratamiento}}" }, "results": { "elegido": "correo" }, "next": "sin_horario" }
```

Entra al componente con sus `params` (textos que admiten variables; dentro del componente se leen como `{{param.nombre}}`). Cuando el componente termina con un Terminar `return`, sigue por `results[resultado]`; si el resultado no está en `results` (o regresó sin resultado, o por una salida sin conectar), por `next` («Otro resultado»). Pasa todos los parámetros que declara el componente.

#### Agendar cita (`book`)

Necesita la tarea especial `appointments` y un tipo de cita de [`calendar`](#calendar-agenda-de-citas). Ofrece los próximos horarios libres de ese tipo como botones vigentes y aparta el que elige el contacto.

```json
{
  "type": "book",
  "calendar": "valoracion",
  "say": [{ "text": "Estos son los próximos horarios para tu valoración. ¿Cuál te acomoda? Si prefieres otro día u hora, escríbemelo." }],
  "offer": 3,
  "confirm": [{ "text": "Listo {{nombre}}, te esperamos el {{cita_fecha}} a las {{cita_hora}} en {{cita_lugar}}." }],
  "booked": "cita_lista",
  "declined": "sin_cita",
  "full": "sin_horarios",
  "next": "espera_horario"
}
```

| Campo | Qué hace |
|---|---|
| `calendar` | Id del tipo de cita. |
| `say` | De 1 a 5 contenidos; los horarios van con el último, que necesita texto (hasta 1024 caracteres) y no puede ser un audio. |
| `offer` | Horarios por mensaje, de 1 a 10. Hasta 3 van como botones («jue 5 nov · 17:00»); más, en una lista de WhatsApp que se abre con «Ver horarios». Se reparten entre los próximos días (uno por día y, si sobran, separados dentro del día). |
| `confirm` | Lo que se envía al apartar la cita, con los botones «Cambiar cita» y «Cancelar cita». Usa `{{cita_fecha}}`, `{{cita_hora}}`, `{{cita_tipo}}` y `{{cita_lugar}}`. |
| `booked` | «Agendada»: el nodo que sigue al apartar la cita (por ejemplo, un nodo Acción con la etiqueta y el evento de Meta). |
| `declined` | «No quiso agendar»: si no quiere agendar, dice que ninguno le sirve sin proponer otro momento o, si ya tenía cita, decide conservarla. |
| `full` | «Sin horarios»: si no hay horarios libres (o la agenda está apagada, o el tipo no existe). Sin conectar, se envía `texts.noSlots` y el flujo termina. |
| `next` | «Siguiente»: lo que sigue enseguida mientras los horarios quedan vigentes, normalmente un Esperar con un recordatorio. |

Dentro de un componente, `declined` y `full` sin conectar regresan al flujo que lo usó.

Si el contacto escribe otro día u hora («el jueves en la tarde», «mañana después de las 6», «de 8 de la noche a la 1»), el intérprete lo convierte en fecha y horas y se le ofrecen horarios de ese momento (`texts.options`) o, si no hay, los más cercanos (`texts.noMatch`). Si escribe el texto de un horario o lo nombra («el de las 5»), lo elige. Un horario de un mensaje anterior se intenta apartar igual; si ya no está libre, se le ofrecen otros (`texts.taken`); si ya pasó, los próximos (`texts.expired`). Si una pausa (un Esperar de tiempo) va antes del «Agendar cita» y el contacto pregunta por un día («¿tienen el jueves?»), la pausa se adelanta y los horarios son de ese día. Si el contacto ya tiene una cita, ver «Preguntar antes de cambiar» en [`calendar`](#calendar-agenda-de-citas).

#### Terminar (`end`)

```json
{ "type": "end", "mode": "free", "reply": true }
```

`mode`: `free`, `human`, `silent` o `return` (ver «Al terminar un flujo»). `reply` solo aplica a `free`. `result` solo aplica a `return` y debe estar en los `results` del componente.

#### Acciones

| Acción | Qué hace |
|---|---|
| `{ "type": "tag", "name": "…", "once": false }` | Pone una etiqueta. `name` admite variables (`area-{{area}}` se guarda en minúsculas y con guiones). |
| `{ "type": "untag", "name": "…", "once": false }` | Quita una etiqueta. |
| `{ "type": "set", "field": "…", "value": "…", "once": false }` | Guarda un valor en un dato del contacto (no en `nombre`). `value` admite variables. En un dato `choice`, escríbelo igual que la opción. |
| `{ "type": "meta", "event": "…", "value": "…", "currency": "MXN", "once": false }` | Envía un evento de conversión a Meta (ver «Eventos de Meta»). `value` admite variables, por ejemplo `{{catalogo.precio_numero}}`. |
| `{ "type": "webhook", "name": "…", "once": false }` | Llama al webhook con ese nombre. |
| `{ "type": "handoff" }` | Solo en las intenciones: la intención es urgente y va antes que todo. La IA contesta siguiendo `reply` y el chat pasa a una persona con el flujo en pausa (no entra a su `target`). En un flujo, pasar a una persona es un Terminar `human`, que termina el flujo. |

Van en el nodo Acción (hasta 20) y en las intenciones (hasta 10). `once: true` («Solo la primera vez en este chat») hace que la acción corra una sola vez por chat: en un nodo Acción, la primera vez que el flujo pasa por ese nodo; en una intención, la primera vez que se detecta. Toda etiqueta que usen debe estar en `tags`.

#### Condiciones

Las usa el nodo Condición (`rules[].when`). Cada una revisa una sola cosa:

| `kind` | `op` | Campos | Se cumple cuando… |
|---|---|---|---|
| `datetime` | `before` / `after` | `datetime`: `"AAAA-MM-DDTHH:MM"` | La hora actual, en la zona de la línea, es antes de esa fecha (`before`) o desde ese minuto (`after`). |
| `tag` | `has` / `not_has` | `tag` | El chat tiene (o no tiene) esa etiqueta. |
| `field` | `equals` / `not_equals` / `filled` / `empty` | `field`, `value` | El dato (`nombre`, uno de `fields`, una variable del sistema como `anuncio_id` o `palabra_clave`, una clave `catalogo.…` o, en un componente, un `param.…`) es igual o distinto a `value` (sin importar mayúsculas, acentos ni espacios), tiene valor o está vacío. El `default` de un dato cuenta como valor. |
| `visited` | `yes` / `no` | `flow`, `node` (opcional) | El contacto ya pasó (o no) por ese flujo, o por ese nodo del flujo, en cualquier momento. Solo cuentan como visitados los Mensajes, los Esperar y los Agendar cita: una regla con otro tipo de nodo da un aviso. |
| `appointment` | `upcoming` / `none` / `attended` / `no_show` | — | Con la agenda: tiene una cita próxima, no tiene ninguna, asistió o faltó a su última cita (lo marca el equipo). Sirve para preguntar «¿quieres cambiar la que tienes?» antes de un nodo `book` o para el seguimiento después de la cita. |

### Variables en los textos

Los textos de los mensajes, los títulos de los botones, la `url` de un archivo, el nombre de una etiqueta en `tag` y `untag`, el `value` de `set` y `meta`, los `params` de un componente, las respuestas de `faqs`, el contenido de `knowledge` y las instrucciones (`reply`) de las intenciones admiten variables `{{…}}`:

| Variable | Valor |
|---|---|
| `{{nombre}}` | Nombre de pila: el que dijo en la conversación, el que escribió el equipo o el que dedujo la tarea `inferName`. Vacío si no se sabe. |
| `{{nombre_completo}}` | Nombre completo (con apellidos si se conocen); si solo se sabe el de pila, el de pila. |
| `{{nombre_origen}}` | De dónde salió `{{nombre}}`: `conversacion` (lo dijo), `perfil` (se dedujo de su perfil de WhatsApp) o `equipo`. Vacía si no hay nombre. Sirve en una regla `field` de una Condición. |
| `{{nombre_original}}` | Nombre de su perfil de WhatsApp tal como llegó en su primer mensaje («JuANiT0», «Laurita🌻»). No cambia. |
| `{{<dato>}}` | Un dato del contacto de `fields` (o su `default`). |
| `{{catalogo.<clave>}}` | Un valor del catálogo. |
| `{{param.<nombre>}}` | Dentro de un componente: un parámetro. |
| `{{perfil}}` | Nombre con el que aparece el contacto en la bandeja: el de su perfil de WhatsApp, el que le puso el equipo o su nombre completo en cuanto se sabe (lo dice o lo deduce la tarea especial `inferName`). |
| `{{telefono}}` | Número de WhatsApp del contacto, solo dígitos con código de país. |
| `{{fecha_registro}}` | Fecha en que escribió por primera vez. |
| `{{anuncio}}`, `{{anuncio_id}}` | Título e ID del anuncio de Meta por el que llegó (vacías si escribió directo). |
| `{{negocio}}` | Nombre del negocio en WhatsApp. |
| `{{fecha_hoy}}`, `{{hora}}` | Fecha y hora del envío, en la zona de la línea. |
| `{{palabra_clave}}` | Palabra clave con la que entró al flujo actual (vacía si entró de otra forma). Sirve para saber, en una regla `field`, desde qué anuncio llegó cuando varios usan el mismo flujo. |
| `{{procedencia}}` | De dónde es: «Yucatán, México» o solo el país («Colombia»). La llena la tarea `inferOrigin` (de lo que dice o de su teléfono), la IA cuando lo dice después o el equipo. Vacía si no se sabe. |
| `{{procedencia_pais}}`, `{{procedencia_estado}}` | País y estado por separado, en español. Sirven en una regla `field` (por ejemplo, llevar a cada estado a su sucursal). |
| `{{procedencia_origen}}` | De dónde salió: `conversacion`, `telefono` o `equipo`. |
| `{{cita}}` | Su cita: «Valoración, jueves 5 de noviembre a las 17:00». Vacía si no tiene o se canceló. |
| `{{cita_tipo}}`, `{{cita_fecha}}`, `{{cita_hora}}`, `{{cita_lugar}}` | Tipo, fecha («jueves 5 de noviembre»), hora («17:00», en la zona de la línea) y lugar de su cita. |

Una variable vacía (o que no existe) se quita junto con el espacio que la precede: «Mucho gusto {{nombre}}.» queda «Mucho gusto.». Al inicio de una línea se quita también la coma o el signo que la sigue: «{{nombre}}, te escribo de la clínica» queda «Te escribo de la clínica». En medio de una frase no: «Gracias, {{nombre}}.» quedaría «Gracias,.». Escribe cada texto para que se lea bien sin la variable. En un botón, deja la variable al final del título y úsala con valores cortos, porque el título se corta a 20 caracteres.

### Eventos de Meta

La acción `meta` envía un evento de conversión a Meta si la línea tiene activados los Eventos Meta. Meta lo atribuye al anuncio de clic a WhatsApp por el que llegó el contacto. Solo se aceptan estos nombres:

| `event` | Cuándo usarlo | Monto (`value`) |
|---|---|---|
| `LeadSubmitted` | Dejó sus datos, pidió una cita o una inscripción | No |
| `QualifiedLead` | El prospecto quedó calificado | No |
| `ViewContent` | Vio un producto, servicio o catálogo | Opcional |
| `AddToCart` | Agregó un producto | Opcional |
| `InitiateCheckout` | Empezó a pagar | Opcional |
| `Purchase` | Compró | **Obligatorio** y mayor que 0 |
| `CartAbandoned` | Dejó la compra sin terminar | Opcional |
| `OrderCreated`, `OrderShipped`, `OrderDelivered`, `OrderCanceled`, `OrderReturned` | Etapas de un pedido | `OrderCreated` opcional |
| `RatingProvided`, `ReviewProvided` | Calificó o dejó una reseña | No |

`value` debe ser un número al enviarse: si viene del catálogo, guarda en el catálogo el monto solo con dígitos (`"500"`, no `"$500 MXN"`). Un `Purchase` sin monto válido no se envía (queda anotado en el chat): si el monto sale de un dato que se pide, dale un valor por defecto. `currency` es un código de 3 letras (`MXN` por defecto).

Para no contar dos veces una conversión:

- **El mismo evento con el mismo monto no sale dos veces en 24 horas desde un mismo chat**, aunque lo envíen dos nodos de flujos distintos (por ejemplo, un `Purchase` al confirmar el pago y otro en un flujo de confirmación del equipo). El segundo queda anotado en el chat como no enviado.
- `LeadSubmitted` y `QualifiedLead` se envían una sola vez por cada clic en un anuncio, aunque el contacto pase varias veces por la acción.
- `once: true` sigue siendo por nodo: la acción de ese nodo corre una sola vez por chat. Úsalo en los eventos que no deben repetirse aunque pasen más de 24 horas.

## Patrones

Cómo se arman con nodos las piezas más comunes. Los tres usan el comportamiento del [ejemplo completo](#ejemplo-completo).

### Botones con recordatorios

Un Mensaje con botones cuyo «Siguiente» lleva a la cadena de recordatorios: Esperar → Mensaje → Esperar → Terminar. Si el contacto toca un botón en cualquier momento, el Esperar se cancela y sigue la salida del botón. Si escribe una pregunta, contesta la IA y el tiempo vuelve a contar desde su mensaje. Si no hace nada, a la hora recibe el recordatorio y 5 horas después el flujo termina sin más (`silent`); aun así, si toca un botón al día siguiente (o escribe su texto), sigue su salida.

```json
{
  "id": "promo-ortodoncia",
  "name": "Anuncio de ortodoncia",
  "kind": "flow",
  "description": "Anuncio ORTO26: ofrece la valoración con dos botones y dos recordatorios.",
  "entry": { "keywords": ["ORTO26"], "welcome": false },
  "start": "oferta",
  "nodes": {
    "oferta": {
      "type": "message",
      "say": [
        { "text": "La valoración de ortodoncia cuesta {{catalogo.valoracion}} y se descuenta si inicias el tratamiento. Los planes empiezan en {{catalogo.ortodoncia_mensualidad}}." },
        { "text": "¿Te aparto una valoración?", "delay": 2 }
      ],
      "buttons": [
        { "id": "agendar", "title": "Sí, agendar", "next": "ir_agendar" },
        { "id": "duda", "title": "Tengo una duda", "next": "dudas" }
      ],
      "next": "espera_1"
    },
    "espera_1": { "type": "wait", "minutes": 60, "next": "recordatorio_1" },
    "recordatorio_1": {
      "type": "message",
      "say": [{ "text": "¿Te quedó alguna duda sobre la ortodoncia? Toca «Sí, agendar» y te ayudo con tu valoración." }],
      "next": "espera_2"
    },
    "espera_2": { "type": "wait", "minutes": 300, "next": "cierre" },
    "cierre": { "type": "end", "mode": "silent" },
    "ir_agendar": { "type": "goto", "flow": "agendar", "node": null },
    "dudas": { "type": "end", "mode": "free", "reply": true }
  }
}
```

Si dejas sin conectar el «Siguiente» del último Esperar, el flujo también termina al acabarse el tiempo, como con un Terminar `free` (con seguimiento automático, si está encendido): toda espera tiene salida. Lo que no conviene dejar sin conectar es el «Siguiente» del Mensaje con botones: sin un Esperar, el flujo espera el botón sin límite de tiempo y, mientras tanto, no hay seguimiento automático. Si el menú no necesita recordatorio, conecta su «Siguiente» a un Esperar solo (por ejemplo, de 60 minutos) sin nada después.

### Pedir datos

Una Condición que revisa si ya se tienen los datos, un Mensaje sin botones que pregunta y, directo en su «Siguiente», un Esperar con los datos. El flujo sigue por «Lo obtuvo» en cuanto el intérprete reconoce todos los datos (aunque los diga con otras palabras, en varios mensajes o junto con una pregunta, que la IA contesta primero). Si da uno solo, la IA le pide el que falta; si da un correo que no sirve, se lo vuelve a pedir. «Se acabó el tiempo» decide qué pasa si no contesta. **La Condición de antes es la que evita volver a preguntar** lo que ya dijo: el Mensaje se envía siempre. Si estos datos se piden en varios flujos, haz de estos nodos un componente.

```json
{
  "id": "datos-factura",
  "name": "Datos para la factura",
  "kind": "flow",
  "description": "Pide el nombre y el correo para enviar la factura.",
  "entry": { "keywords": [], "welcome": false },
  "start": "revisa",
  "nodes": {
    "revisa": {
      "type": "condition",
      "rules": [
        { "when": { "kind": "field", "op": "empty", "field": "nombre" }, "next": "pregunta" },
        { "when": { "kind": "field", "op": "empty", "field": "correo" }, "next": "pregunta" }
      ],
      "else": "gracias"
    },
    "pregunta": {
      "type": "message",
      "say": [{ "text": "Con gusto te enviamos tu factura. ¿Me compartes tu nombre completo y tu correo?" }],
      "next": "espera"
    },
    "espera": { "type": "wait", "minutes": 120, "for": { "fields": ["nombre", "correo"] }, "got": "gracias", "next": "sin_datos" },
    "gracias": {
      "type": "message",
      "say": [{ "text": "Gracias {{nombre}}. Recepción te envía la factura a {{correo}} hoy mismo." }],
      "next": "fin"
    },
    "sin_datos": {
      "type": "message",
      "say": [{ "text": "Cuando tengas tus datos a la mano, escríbemelos por aquí y preparamos tu factura." }],
      "next": "fin"
    },
    "fin": { "type": "end", "mode": "free" }
  }
}
```

Un dato con captura `asked` solo se guarda mientras un Esperar lo espera; uno con captura `always` se guarda en cualquier momento (y la Condición de antes evita volver a preguntarlo).

### Esperar una confirmación del equipo

Para lo que solo el equipo puede confirmar (un pago, un documento revisado), un Esperar de etiqueta: el flujo sigue por «Se cumplió» en cuanto alguien del equipo pone la etiqueta en el chat (o la pone la API), y por «Se acabó el tiempo» si no llega. Los nodos Acción ponen la etiqueta de pendiente, la quitan al confirmar y envían los eventos de Meta. Estos son los nodos centrales del flujo `anticipo` del ejemplo completo:

```json
{
  "pendiente": {
    "type": "action",
    "actions": [
      { "type": "tag", "name": "anticipo-pendiente" },
      { "type": "meta", "event": "InitiateCheckout", "value": "{{catalogo.anticipo_monto}}", "currency": "MXN", "once": true }
    ],
    "next": "enlace"
  },
  "enlace": {
    "type": "message",
    "say": [{ "text": "El blanqueamiento se aparta con un anticipo de {{catalogo.anticipo}}, que se descuenta del precio. Puedes pagarlo aquí: {{catalogo.enlace_anticipo}}\nEn cuanto recepción lo confirme, te aviso por este medio." }],
    "next": "espera_pago"
  },
  "espera_pago": { "type": "wait", "minutes": 180, "for": { "tag": "anticipo-confirmado" }, "got": "confirmado", "next": "pregunta_pago" },
  "confirmado": {
    "type": "action",
    "actions": [
      { "type": "untag", "name": "anticipo-pendiente" },
      { "type": "meta", "event": "Purchase", "value": "{{catalogo.anticipo_monto}}", "currency": "MXN" }
    ],
    "next": "gracias"
  }
}
```

Mientras espera, si el contacto escribe («ya pagué, ¿me confirman?»), la IA le contesta sin prometer tiempos ni confirmar nada, y el Esperar sigue. Si recepción pasa el chat a Persona para hablar con el contacto, el flujo queda en pausa: al devolverlo al bot con la etiqueta ya puesta, sigue por «Se cumplió»; sin ella, el Esperar vuelve a contar sus 3 horas desde ese momento. Para que un comprobante que envía el contacto haga seguir el flujo sin el equipo, usa en su lugar un dato de tipo Archivo y un Esperar de ese dato (una foto o un PDF lo cumplen; un sticker o una nota de voz, no).

## Cómo diseñar

### Qué va en un flujo y qué en la IA

- **Flujo** para lo que siempre debe pasar igual: bienvenida, menú, presentación de un producto, toma de datos, confirmación, pasar el chat a una persona.
- **IA** para lo que no se puede prever: preguntas, objeciones, dudas fuera del menú. Dale la información en `knowledge`, `faqs` y `catalog`; el flujo sigue esperando mientras ella contesta.
- **Intenciones** para conectar las dos: lo que el contacto puede pedir en cualquier momento («quiero agendar», «me duele una muela», «ya no me interesa»).

### Buenas prácticas

- **Un solo flujo de bienvenida.** Es el que recibe a los contactos nuevos que no traen palabra clave. Solo uno puede tener `"welcome": true`.
- **Palabras clave poco comunes.** Una palabra clave se activa aunque esté a media frase, saca al contacto de lo que estaba haciendo y descarta el flujo suspendido: con `CURSO`, «mejor sigamos con el curso» empieza ese flujo de nuevo. Usa la del texto prellenado del anuncio (`INFOPROMO`, no `precio`). La importación y el editor avisan si una palabra clave es una palabra común («hola», «info», «precio», «curso», «cita», «pago»…).
- **Sin ciclos automáticos.** No hagas que un flujo vuelva a un nodo anterior sin que el contacto intervenga (por ejemplo, Condición → Mensaje → Condición): se detiene. Para volver a un menú, usa un botón.
- **Recordatorios dentro de las 24 horas.** La suma de los Esperar de una cadena de recordatorios debe quedar por debajo de 24 horas (mejor, unas pocas horas): después WhatsApp ya no deja escribir, y lo que el horario de atención retiene hasta la mañana no sale si para entonces ya pasaron las 24 horas. Uno o dos recordatorios bastan; el último puede llevar a un cierre (Terminar `silent`).
- **Cada menú con salida.** Conecta el «Siguiente» de cada Mensaje con botones a un Esperar (con o sin recordatorios). Sin él, el chat se queda en el flujo esperando el botón para siempre, sin seguimiento automático.
- **Prueba los cambios grandes en una copia desactivada.** Con «Guardar como copia desactivada» (o `"active": false` en el paquete) preparas la versión nueva sin tocar la que atiende, la pruebas iniciándola a mano desde un chat y después cambias cuál está activa.
- **Pide los datos con Condición → Mensaje → Esperar**: la Condición evita volver a preguntar lo que ya se sabe (el Mensaje siempre se envía); el flujo sigue en cuanto el intérprete los reconoce y la IA los vuelve a pedir si no sirven. Si el mismo dato se pide en varios flujos, hazlo componente.
- **Botones cuando la respuesta es una de pocas opciones.** Son más fiables que un dato `choice` escrito. Si usas un `choice`, describe en `description` qué frases corresponden a cada opción.
- **Las acciones en su propio nodo.** Después del botón que muestra interés, un nodo Acción con la etiqueta y el evento de Meta (con `once` si no debe repetirse).
- **Usa el catálogo** para todo precio, fecha, cupo, enlace o dirección, y escríbelo en los textos como `{{catalogo.clave}}`. Así la IA y los flujos dicen siempre lo mismo y se actualiza en un solo lugar. La fecha de una condición `datetime` se escribe en la condición: mantenla igual que la del catálogo y dilo en su `description`.
- **Componentes para no duplicar.** Si varios flujos hacen lo mismo (elegir horario, pedir datos de facturación), hazlo un componente con `params` para lo que cambia y `results` para cada forma de terminar.
- **Intención con destino o con instrucción.** Con `target` cuando la respuesta es siempre la misma secuencia (agendar, pasar a una persona, despedirse y cerrar). Con `reply` cuando basta con que la IA conteste de cierta forma. Si el contacto pide que no le escriban, usa un destino que se despida y termine con un Terminar `silent`, que no programa el seguimiento automático.
- **Intenciones que sirven a media conversación.** Todas aplican también con un flujo en curso: descríbelas para que no se activen con lo que ese flujo ya resuelve (un botón, un dato) y haz que su flujo sea corto y termine, para que el anterior siga. Ver [Intenciones y caminos del flujo](#intenciones-y-caminos-del-flujo).
- **Ramifica por anuncio** con una condición `field` sobre `anuncio_id` (no cambia aunque cambie el título del anuncio) para llevar a cada contacto al producto de su campaña aunque haya borrado el texto prellenado.
- **Captura `always`** para los datos que el contacto suele decir solo (el producto que le interesa, su correo), y `asked` para los que solo tienen sentido como respuesta a una pregunta.
- **Pasar a una persona** con un Mensaje de aviso y después un Terminar `human`. En una intención, `handoff` con `reply` hace que la IA avise y luego pase el chat; el flujo en curso queda en pausa y sigue al devolver el chat al bot.
- **Confirmaciones del equipo** (un pago, un documento) con un Esperar de etiqueta: el equipo pone la etiqueta y el flujo sigue solo.
- **Diseña cada botón pensando que se puede tocar días después.** Si una oferta vence, pon una Condición por fecha al inicio de esa rama.
- **No describas los flujos en `persona`.** La IA ve sus mensajes y botones en el historial y recibe en qué parte del flujo está el contacto.
- **Citas con un flujo para agendar.** Un flujo corto (`book` → despedida), puesto en `calendar.flow` y como destino de una palabra clave o de la bienvenida. Que termine pronto: si el contacto pide agendar a media conversación, el flujo en que estaba queda suspendido y sigue al terminar el de agendar. No hace falta preguntar si quiere cambiar la cita que ya tiene: si elige otro horario, el sistema se lo pregunta. Conecta `full` a un mensaje que ofrezca pasar con una persona y `declined` a uno que deje la puerta abierta; en `next`, un Esperar con un recordatorio. Los recordatorios de la cita van en `calendar.reminders`, no en un Esperar. Si dos tipos de cita los atiende la misma persona, dales la misma `agenda`.

### Intenciones y caminos del flujo

Todas las intenciones aplican también a media conversación y el intérprete ve lo que dirá el flujo (ver [Caminos del flujo](#caminos-del-flujo) y [Flujo suspendido](#flujo-suspendido)). Para aprovecharlo:

- **Pon la respuesta probable después de una pausa.** Si lo que el contacto suele preguntar enseguida (el precio, la fecha, cómo pagar) está en el Mensaje que sigue a un Esperar sin `for`, y lo pregunta antes de que pase el tiempo, el flujo se adelanta: sale ese Mensaje en lugar de una respuesta de la IA, y no se repite después. Por ejemplo: Mensaje con la presentación → Esperar 10 minutos → Mensaje con el precio y las formas de pago → Mensaje con botones.
- **Marca «La IA puede elegirlo» solo en los botones que informan.** En el editor, cada botón tiene la casilla «La IA puede elegirlo si el contacto pide lo que ofrece», apagada por defecto (`"aiPick": true` en el paquete). Con ella, si el contacto no toca el botón pero pide justo lo que ofrece («¿qué temas ven?» con un botón «Ver temario»), el flujo sigue por ese botón en lugar de que conteste la IA, y no se repite después. Márcala en botones como «Ver temario», «Precios», «Horarios» o «Ubicación». **No la marques** en los que confirman, aceptan, pagan o deciden algo («Sí, inscribirme», «Ya me inscribí», «Ya pagué», «Confirmo»), aunque su camino solo muestre otro menú: solo tú sabes lo que significa el botón, y si la IA lo eligiera por el contacto le contestaría como si ya lo hubiera hecho. El sistema no deja marcar un botón cuyo camino pone etiquetas, guarda datos, envía eventos, agenda o pasa a una persona (también si eso pasa después de una pausa): el editor deshabilita la casilla y dice por qué, y la validación lo rechaza. Títulos claros, que digan lo que ofrecen, ayudan a la IA a reconocerlos.
- **Flujos de intención cortos y que terminen.** Una intención con destino a media conversación suspende el flujo en que estaba el contacto. Para que ese flujo siga, el de la intención debe terminar con un Terminar `free` o una salida sin conectar (un Mensaje con la respuesta y nada más basta). Si el flujo de la intención escucha (un menú, un Esperar), el anterior espera a que termine. Usa `silent` («Terminar sin más») solo cuando el flujo anterior no debe seguir, como en una baja («ya no me escriban»).
- **Una intención hacia el flujo en curso no mueve al contacto.** Si el contacto ya está en el flujo `agendar`, una intención con destino `agendar` (o `agendar:correo`) no lo mueve: se ejecutan sus acciones, contesta la IA y el flujo sigue donde va. Así una intención no saca al contacto de lo que el flujo espera (un pago, un dato). Si quieres que una intención lleve a otra parte de un flujo largo, pon esa parte en un flujo aparte y apunta la intención ahí: entonces el flujo en curso queda suspendido y sigue al terminar.
- **Urgencias y quejas: una intención con «Pasar a una persona»** (`handoff`) y una `reply` que le avise al contacto, sin `target` (como `urgencia` en el [ejemplo completo](#ejemplo-completo)). Va antes que todo, aunque el mismo mensaje también elija un botón o dé el dato que se espera, y el flujo queda en pausa: sigue donde iba al devolver el chat al bot. Una intención con destino a un flujo que avisa y termina con `human` (como `asesor`, que lleva al flujo `persona`) sirve para un aviso fijo, pero va en su lugar del orden: si el mismo mensaje también contesta lo que espera el flujo, gana el flujo. En los dos casos, el flujo anterior sigue al devolver el chat al bot.
- **Las palabras clave reemplazan.** Una palabra clave inicia su flujo y descarta el flujo suspendido: es un inicio explícito. Un botón de un mensaje anterior, en cambio, depende de su flujo: del flujo en curso, se mueve dentro de él; del suspendido, lo retoma; de otro flujo, empieza de nuevo y descarta el suspendido.
- **Un solo nivel y 24 horas.** Solo se recuerda un flujo suspendido: si el contacto pasa por dos intenciones seguidas, al terminar la segunda vuelve al flujo en que estaba antes de la primera. Si pasan más de 24 horas desde que se suspendió, ya no se retoma; por eso conviene que los flujos de intención no tengan esperas largas. Lo que el contacto da mientras tanto y pedía el flujo suspendido (un correo, por ejemplo) sí se guarda.
- **Revísalo en el chat y en la bitácora.** Mientras el contacto está en el flujo de una intención, la cabecera del chat agrega ««Curso» sigue al terminar este». La bitácora anota cuándo un flujo queda suspendido, cuándo sigue donde estaba, cuándo se descarta (con el motivo) y cuándo una pausa se adelanta. Con la API, `POST /v1/simulate` lo muestra en `summary.suspended` y en `log`.

### Varios contenidos en un Mensaje o Mensaje → Esperar

Hay dos formas de que un mensaje salga después de otro, y no son lo mismo:

- **Varios contenidos en un mismo Mensaje** (con `delay` de 0 a 30 segundos entre ellos) es el **comodín para que salgan completos y en orden, pase lo que pase**. El flujo da el nodo por enviado de una vez y sigue al nodo siguiente; el `delay` solo espacia la entrega. Si el contacto escribe en medio, lo que falta **no se pausa ni se cancela**: sale a su hora, y la respuesta de la IA (si la hay) sale después. Solo lo cancelan pasar el chat a una persona, «Detener», el límite de mensajes automáticos o que se cierre la ventana de 24 horas.
- **Mensaje → Esperar (solo tiempo) → Mensaje** es una **pausa del flujo**: mientras corre, el flujo escucha. Si el contacto pregunta justo lo que dice el Mensaje siguiente, se adelanta (sale en ese momento y la IA no lo repite); si pregunta otra cosa, contesta la IA y el Mensaje sale cuando vence el tiempo (ver [Caminos del flujo](#caminos-del-flujo)).

**Usa varios contenidos en un Mensaje cuando el orden importa más que la conversación:**

- **Un archivo y su explicación.** Una imagen, un video o un PDF (el temario, la ficha del producto) y, unos segundos después, el texto que lo explica o lo que hay que hacer con él. Si el archivo es pesado, el `delay` ayuda a que el texto no llegue antes.
- **Datos que no se deben cortar.** Los datos para pagar (banco, CLABE, referencia) seguidos de «Cuando pagues, envíame tu comprobante»; las condiciones de una promoción; instrucciones paso a paso; un aviso legal o de privacidad.
- **Ritmo de conversación.** Partir un texto largo en dos o tres mensajes cortos con 2 a 5 segundos entre ellos, como si alguien escribiera.
- **El cierre con botones.** Varios contenidos con los botones en el último: los botones siempre llegan al final, después de lo que los explica.

**Usa Mensaje → Esperar → Mensaje cuando el segundo mensaje responde algo que el contacto suele preguntar** (el precio, las fechas, cómo pagar) o depende de lo que conteste («¿Sigues ahí?»). Con el comodín, ni el intérprete ni la IA saben que ese contenido está por salir: si el contacto pregunta justo eso en medio, la IA podría contestarlo y el contacto lo recibiría dos veces.

Ten en cuenta:

- **Retrasos cortos.** El máximo es 30 segundos por contenido, y mientras tanto la respuesta de la IA espera detrás: con varios contenidos largos, el contacto puede sentir que no le contestan. Para esperas de más de unos segundos, usa un Esperar.
- **El tiempo de un Esperar que sigue al Mensaje cuenta desde que se ejecuta el Mensaje**, no desde que llega su último contenido: con dos contenidos (el segundo a 30 segundos) y un Esperar de 1 minuto, entre el segundo contenido y lo que sigue pasan unos 30 segundos.

### Cómo redactar el comportamiento

- **Instrucciones directas y breves.** Una frase clara funciona mejor que un párrafo enfático. Evita las MAYÚSCULAS, los «NUNCA» y los «MUY IMPORTANTE».
- **Di qué hacer, no solo qué no hacer.** «Si no tienes un dato, dilo y ofrece averiguarlo» funciona mejor que «No inventes».
- **Define el rol y sus límites** en `persona`: quién es, para quién trabaja, qué no hace y qué hace en su lugar.
- **Pide brevedad** («de una a tres frases», «una pregunta por mensaje») y texto plano: WhatsApp no es un correo.
- **Ordena por prioridad** objetivos y reglas. La primera regla, la de seguridad o escalamiento si la hay.
- **Un dato, un lugar.** Precios y fechas en `catalog`; políticas y detalles en `knowledge`; respuestas exactas en `faqs`. No repitas la misma instrucción en varias secciones.
- **Títulos de tema descriptivos.** El intérprete elige los temas solo por su `topic`: «Ortodoncia: opciones y planes» se elige mejor que «Info 2». Un tema por asunto.
- **Intenciones observables** («Pide hablar con una persona»), no estados internos («Está molesto»), con ejemplos variados.
- **No prometas lo que el sistema no hace.** La IA no tiene acceso a inventario ni pagos y no agenda por su cuenta: reúne los datos y avisa que el equipo confirmará. Si el negocio agenda citas, enciende `appointments` y usa un nodo `book`: el sistema ofrece los horarios reales y aparta la cita.
- **Negocios sensibles** (salud, legal, finanzas): agrega la regla de qué hacer ante una urgencia y una intención con «Pasar a una persona» (`handoff`) que la lleve al equipo.

## Lista de verificación

- [ ] `format` es `"superchat.agent"` y `version` es `3`. El JSON es válido, sin comentarios, y solo usa los campos de esta guía.
- [ ] Todos los datos vienen del contexto o de las respuestas del usuario: no hay precios, fechas, horarios ni enlaces inventados.
- [ ] `persona` tiene texto; objetivos y reglas están en orden de prioridad.
- [ ] Los precios, fechas, cupos y enlaces están en `catalog` y los textos los usan como `{{catalogo.clave}}`.
- [ ] Toda `{{variable}}` es del sistema, un dato de `fields`, una clave del catálogo o (en un componente) un parámetro, y el texto se lee bien si queda vacía. Un botón con variables no pasa de 20 caracteres con sus valores.
- [ ] Todo dato `choice` tiene `options`; los `set` sobre un dato `choice` usan una de sus opciones; ningún dato se llama como una variable del sistema.
- [ ] Toda etiqueta que usan los nodos Acción, los Esperar y las intenciones está en `tags` con su `use`.
- [ ] Cada intención tiene `description` y `target`, `reply` o `actions` (no `target` y `reply` juntos; una con `handoff`, con `reply` y sin `target`); cada `target` apunta a un flujo del paquete (no a un componente) y, si lleva nodo, a uno que existe; ninguna depende de saltar dentro del flujo en que ya está el contacto (ahí no lo mueve); sus acciones usan eventos, webhooks y datos que existen; ninguna trae `"interrupts"` (ya no se usa); los flujos de destino son cortos y terminan (con `free`, una salida sin conectar o `human`; `silent` solo en una baja).
- [ ] Como mucho un flujo activo tiene `"welcome": true`; las palabras clave son poco comunes (ninguna es una palabra común como «hola», «info», «precio», «curso», «cita» o «pago»: la importación lo avisa), de 2 letras o más y ninguna está en dos flujos activos (sin importar mayúsculas, acentos ni signos).
- [ ] `specialTasks` solo enciende tareas de la tabla (`inferName`, `inferOrigin`, `appointments`), con `true` o `false`. Si `inferName` está encendida, la bienvenida tiene en cuenta que `{{nombre}}` puede llegar lleno (ver `{{nombre_origen}}`).
- [ ] Si hay nodos `book`: `appointments` está encendida, `calendar` trae sus tipos de cita (o la línea ya los tiene), el último contenido de `say` tiene texto, `offer` va de 1 a 10, `calendar.flow` apunta a un flujo activo con un nodo `book` y ningún día tiene franjas encimadas. Si la línea ya usa la agenda y el paquete no la trae, `specialTasks` no apaga `appointments`.
- [ ] Cada flujo tiene `start` y cada salida (`next`, `got`, `else`, los botones, las reglas y los `results`) apunta a un nodo que existe en ese flujo o es `null`; se llega a todos los nodos desde el inicio.
- [ ] Ningún flujo vuelve a un nodo anterior sin que el contacto intervenga.
- [ ] Los Mensajes con botones tienen texto en el último contenido (que no es audio, y de hasta 1024 caracteres), de 1 a 3 botones con títulos de hasta 20 caracteres, distintos entre sí, e ids distintos (ninguno es `cita_si` ni `cita_no`), y su «Siguiente» va a un Esperar.
- [ ] Cada pedido de datos es un Mensaje sin botones con un Esperar de datos directo en su «Siguiente».
- [ ] Cada Esperar dura minutos enteros, de 1 a 43 200 (o `null` si espera algo); los de una cadena de recordatorios suman menos de 24 horas; los datos y etiquetas que espera existen; las fechas usan `AAAA-MM-DDTHH:MM` en la zona de la línea.
- [ ] Los nodos «Ir a» apuntan a flujos (no componentes); los nodos Componente pasan todos sus parámetros y solo usan resultados que el componente declara; `return` solo aparece en componentes; `handoff` solo en las intenciones.
- [ ] Los eventos de Meta usan nombres de la tabla y `Purchase` lleva un monto numérico.
- [ ] Todo archivo tiene `url` y `transcription`.

## Ejemplo completo

Clínica Dental Alba, un consultorio dental. El paquete tiene:

- **Catálogo** con precios, la vigencia de una promoción, el anticipo del blanqueamiento con su enlace, el horario, la dirección, el mapa y el teléfono.
- **Datos del contacto:** `tratamiento` (`choice`, captura `always`: si el contacto dice en cualquier momento «me interesa la ortodoncia», se guarda), `horario_preferido` y `correo` (captura `asked`).
- **Bienvenida** (`bienvenida`, también con la palabra clave `menu`): un menú con tres botones y dos recordatorios (el segundo se despide y termina con `silent`). «Limpieza dental» y «Ortodoncia» pasan por un nodo Acción que pone una etiqueta, guarda el tratamiento y envía `ViewContent` a Meta una sola vez. La rama de limpieza usa una **Condición** por fecha para mostrar la promoción solo hasta el 30 de noviembre. «Tengo una duda» termina con un Terminar `free` con `reply: true` para que la IA conteste enseguida.
- **Agendar** (`agendar`): pide el nombre y el tratamiento con Mensaje → Esperar, con una Condición antes de cada pregunta que la salta si ya lo dijo (el nombre deducido del perfil de WhatsApp no cuenta), usa el **componente** `elegir-horario` con el parámetro `tratamiento` y, si regresa con el resultado `elegido`, pide el correo, pone la etiqueta `cita-solicitada`, envía `LeadSubmitted`, resume la solicitud y pasa el chat a recepción con un Terminar `human`. Si el tratamiento es blanqueamiento, antes pasa al flujo `anticipo`. Si no eligió horario, termina con `free`.
- **Componente** `elegir-horario`: tres botones que guardan el horario (un nodo Acción por botón) y regresan con `return` y el resultado `elegido`; si no contesta, un recordatorio y después regresa sin resultado.
- **Anticipo** (`anticipo`): envía el enlace del anticipo y **espera la etiqueta** `anticipo-confirmado`, que pone recepción; al llegar, quita `anticipo-pendiente`, envía `Purchase` y pasa el chat a recepción. Si no llega en 3 horas, pregunta si pudo pagar (con dos botones) y sigue esperando la etiqueta 10 horas más; si tampoco llega, el flujo termina y la conversación queda libre.
- **Persona** (`persona`): avisa que en unos minutos le escribe una persona del equipo y termina con `human`. Lo usa la intención `asesor`.
- **Intenciones** (todas aplican también a media conversación):
  - `agendar`, con destino `agendar` (si el contacto ya está en ese flujo, no lo mueve: contesta la IA y el flujo sigue donde va);
  - `urgencia`, con «Pasar a una persona» (`handoff`): pone su etiqueta, la IA avisa siguiendo su `reply` (con el teléfono del catálogo) y el chat pasa al equipo. Va antes que todo y el flujo en que estaba el contacto queda en pausa;
  - `asesor`, con destino al flujo `persona`, que avisa y pasa el chat al equipo;
  - `precios`, con instrucción: el contacto sigue en su flujo;
  - `no_interesa`, con destino `baja`, que se despide y termina con `silent`: sin seguimiento automático, y el flujo en que estaba el contacto ya no sigue.

```json
{
  "format": "superchat.agent",
  "version": 3,
  "behavior": {
    "persona": "Eres Valeria, recepcionista de Clínica Dental Alba, un consultorio dental en Zapopan, Jalisco. Atiendes por WhatsApp a pacientes y a personas que escriben por primera vez: resuelves sus dudas sobre tratamientos, precios y horarios, y les ayudas a pedir una cita.\n\nTono: cálido y profesional. Tuteas, escribes mensajes de una a tres frases en texto plano y haces una sola pregunta por mensaje.\n\nNo eres dentista: no das diagnósticos ni recomiendas medicamentos; si te preguntan algo clínico, explica que la dentista lo revisa en la valoración y ofrece agendarla. Tampoco confirmas días ni horas: recepción confirma cada cita por este medio.",
    "goals": [
      "Que la persona pida una cita: saber su nombre, el tratamiento que le interesa y el horario que prefiere.",
      "Resolver con precisión las dudas sobre tratamientos, precios, horarios, ubicación y formas de pago.",
      "Reconocer las urgencias (dolor fuerte, hinchazón, sangrado o un diente roto) para que el equipo las atienda de inmediato."
    ],
    "rules": [
      "Si la persona describe una urgencia, no sigas con la venta: dile que la atendemos hoy y que una persona del equipo le escribe enseguida.",
      "Usa los precios, fechas y enlaces del catálogo tal cual. Si un dato no está, dilo y ofrece que recepción lo confirme.",
      "Si un mensaje del flujo acaba de dar información o mostrar botones, no la repitas: responde solo lo que preguntó.",
      "Haz una sola pregunta por mensaje."
    ],
    "faqs": [
      { "question": "¿Atienden a niños?", "answer": "Sí, atendemos a niños desde los 3 años. Su primera visita es una valoración tranquila para que conozcan el consultorio." },
      { "question": "¿Duele la limpieza dental?", "answer": "Normalmente no: se siente presión y algo de cosquilleo. Si tienes sensibilidad, la dentista aplica un gel para que estés cómoda." },
      { "question": "¿Puedo pagar a meses?", "answer": "Sí: 3 y 6 meses sin intereses con tarjetas de crédito participantes, en tratamientos desde $3,000 MXN." },
      { "question": "¿Dan factura?", "answer": "Sí, emitimos factura (CFDI) si la pides el mismo día del pago con tus datos fiscales." }
    ],
    "knowledge": [
      { "topic": "Tratamientos: limpieza, blanqueamiento y revisión general", "content": "Limpieza dental: ultrasonido, pulido y revisión general; dura unos 45 minutos y se recomienda cada 6 meses. Blanqueamiento: en consultorio, una sesión de 90 minutos; requiere una limpieza previa de no más de 3 meses y se aparta con un anticipo. Revisión general: valoración con la dentista, radiografía panorámica si hace falta y plan de tratamiento por escrito." },
      { "topic": "Ortodoncia: opciones y planes", "content": "Brackets metálicos, brackets estéticos (cerámicos) y alineadores transparentes. Antes de cualquier opción se hace una valoración con estudios. El tratamiento dura de 12 a 24 meses según el caso y se paga con un enganche y mensualidades." },
      { "topic": "Citas, cancelaciones y llegada", "content": "Las citas se confirman por WhatsApp con un día de anticipación. Para cancelar o cambiar una cita, avisa con al menos 24 horas. Llega 10 minutos antes; hay estacionamiento gratuito en la plaza." }
    ],
    "fields": [
      { "name": "tratamiento", "label": "Tratamiento de interés", "type": "choice", "options": ["limpieza", "blanqueamiento", "ortodoncia", "revisión general"], "description": "Tratamiento que le interesa al paciente. «Brackets» o «alineadores» es ortodoncia; «que me revisen» o «una valoración» es revisión general.", "capture": "always", "default": "revisión general" },
      { "name": "horario_preferido", "label": "Horario preferido", "type": "choice", "options": ["mañana", "tarde", "sábado"], "description": "Horario que prefiere para su cita.", "capture": "asked", "default": "" },
      { "name": "correo", "label": "Correo", "type": "email", "description": "Correo para enviarle la confirmación de su cita o su factura.", "capture": "asked", "default": "" }
    ],
    "tags": [
      { "name": "interes-limpieza", "use": "Tocó «Limpieza dental» en el menú." },
      { "name": "interes-ortodoncia", "use": "Tocó «Ortodoncia» en el menú." },
      { "name": "cita-solicitada", "use": "Pidió una cita: recepción debe confirmar el día y la hora." },
      { "name": "anticipo-pendiente", "use": "Recibió el enlace del anticipo del blanqueamiento y aún no se confirma." },
      { "name": "anticipo-confirmado", "use": "Recepción confirmó el anticipo. Al ponerla, el flujo «Anticipo» sigue si estaba esperando el pago." },
      { "name": "revisar-pago", "use": "Dice que ya pagó el anticipo: recepción debe revisarlo." },
      { "name": "urgencia", "use": "Describió dolor fuerte, hinchazón, sangrado o un diente roto." },
      { "name": "no-interesa", "use": "Dijo que ya no le interesa: no insistir." }
    ],
    "catalog": [
      { "key": "valoracion", "value": "$350 MXN", "description": "Valoración inicial; se descuenta si inicia un tratamiento." },
      { "key": "limpieza", "value": "$650 MXN", "description": "Precio normal de la limpieza dental." },
      { "key": "limpieza_promo", "value": "$490 MXN", "description": "Precio de la limpieza durante la promoción de noviembre." },
      { "key": "promo_hasta", "value": "30 de noviembre de 2026", "description": "Último día de la promoción de limpieza. El nodo «promo» del flujo Bienvenida usa la misma fecha." },
      { "key": "blanqueamiento", "value": "$3,200 MXN", "description": "Blanqueamiento en consultorio, una sesión." },
      { "key": "anticipo", "value": "$500 MXN", "description": "Anticipo para apartar el blanqueamiento; se descuenta del precio." },
      { "key": "anticipo_monto", "value": "500", "description": "El anticipo en número, para los eventos de Meta." },
      { "key": "enlace_anticipo", "value": "https://clinica-alba.example/anticipo", "description": "Enlace para pagar el anticipo." },
      { "key": "ortodoncia_mensualidad", "value": "$1,400 MXN al mes", "description": "Mensualidad desde la que empiezan los planes de ortodoncia." },
      { "key": "horario", "value": "lunes a viernes de 9:00 a 19:00 y sábados de 9:00 a 14:00", "description": "Horario de atención." },
      { "key": "direccion", "value": "Av. Patria 1450, local 8, Zapopan, Jalisco", "description": "Dirección del consultorio." },
      { "key": "ubicacion", "value": "https://clinica-alba.example/ubicacion", "description": "Enlace al mapa." },
      { "key": "telefono", "value": "33 1234 5678", "description": "Teléfono del consultorio para urgencias en horario de atención." }
    ],
    "intents": [
      {
        "id": "agendar",
        "description": "Quiere agendar, pedir o cambiar una cita o una valoración.",
        "examples": ["Quiero una cita", "¿Tienen espacio el sábado?", "Me gustaría ir a revisión"],
        "target": "agendar"
      },
      {
        "id": "urgencia",
        "description": "Describe dolor fuerte, hinchazón, sangrado o un diente roto o caído.",
        "examples": ["Me duele muchísimo una muela", "Se me rompió un diente", "Tengo la cara hinchada"],
        "reply": "Dile que lamentamos la molestia y que las urgencias las atendemos hoy mismo: si puede, que nos llame al {{catalogo.telefono}}; una persona del equipo también le escribe por aquí en unos minutos.",
        "actions": [{ "type": "tag", "name": "urgencia" }, { "type": "handoff" }]
      },
      {
        "id": "asesor",
        "description": "Pide hablar con una persona o tiene una queja.",
        "examples": ["¿Me puede atender alguien?", "Quiero hablar con la doctora"],
        "target": "persona"
      },
      {
        "id": "precios",
        "description": "Pregunta cuánto cuesta un tratamiento.",
        "examples": ["¿Cuánto cuesta la limpieza?", "Precio del blanqueamiento"],
        "reply": "Da el precio del catálogo del tratamiento que pregunta en una o dos frases y ofrece agendar una valoración."
      },
      {
        "id": "no_interesa",
        "description": "Dice que ya no le interesa o pide que no le escriban más.",
        "examples": ["Ya no me interesa, gracias", "Ya no me manden mensajes"],
        "target": "baja",
        "actions": [{ "type": "tag", "name": "no-interesa" }]
      }
    ],
    "webhooks": [],
    "specialTasks": { "inferName": false, "inferOrigin": false, "appointments": false }
  },
  "intervals": {
    "timezone": "America/Mexico_City",
    "receptionTimeMin": 6,
    "receptionTimeMax": 12,
    "splitReplies": true,
    "splitMin": 1,
    "splitMax": 2,
    "loopMinAge": 240,
    "scheduleEnabled": true,
    "scheduleStart": "09:00",
    "scheduleEnd": "19:00"
  },
  "flows": [
    {
      "id": "bienvenida",
      "name": "Bienvenida",
      "kind": "flow",
      "description": "Menú para los contactos nuevos y para quien escribe «menú».",
      "entry": { "keywords": ["menu"], "welcome": true },
      "start": "menu",
      "nodes": {
        "menu": {
          "type": "message",
          "say": [{ "text": "¡Hola! Soy Valeria, de Clínica Dental Alba. ¿En qué te puedo ayudar?" }],
          "buttons": [
            { "id": "limpieza", "title": "Limpieza dental", "next": "limpieza" },
            { "id": "ortodoncia", "title": "Ortodoncia", "next": "ortodoncia" },
            { "id": "cita", "title": "Agendar cita", "next": "ir_agendar" }
          ],
          "next": "espera_menu"
        },
        "espera_menu": { "type": "wait", "minutes": 60, "next": "recordatorio_menu" },
        "recordatorio_menu": {
          "type": "message",
          "say": [{ "text": "¿Sigues por aquí? Toca una opción o escríbeme tu duda." }],
          "next": "espera_cierre"
        },
        "espera_cierre": { "type": "wait", "minutes": 300, "next": "despedida_menu" },
        "despedida_menu": {
          "type": "message",
          "say": [{ "text": "Te dejo nuestro horario: {{catalogo.horario}}. Cuando quieras agendar, escríbeme por aquí." }],
          "next": "cierre"
        },
        "cierre": { "type": "end", "mode": "silent" },
        "limpieza": {
          "type": "action",
          "actions": [
            { "type": "tag", "name": "interes-limpieza" },
            { "type": "set", "field": "tratamiento", "value": "limpieza" },
            { "type": "meta", "event": "ViewContent", "once": true }
          ],
          "next": "promo"
        },
        "promo": {
          "type": "condition",
          "rules": [
            { "when": { "kind": "datetime", "op": "before", "datetime": "2026-12-01T00:00" }, "next": "limpieza_promo" }
          ],
          "else": "limpieza_normal"
        },
        "limpieza_promo": {
          "type": "message",
          "say": [{ "text": "Este mes la limpieza dental con ultrasonido y pulido cuesta {{catalogo.limpieza_promo}} en lugar de {{catalogo.limpieza}}. La promoción es hasta el {{catalogo.promo_hasta}}." }],
          "next": "agendar_pregunta"
        },
        "limpieza_normal": {
          "type": "message",
          "say": [{ "text": "La limpieza dental con ultrasonido y pulido cuesta {{catalogo.limpieza}} e incluye una revisión general." }],
          "next": "agendar_pregunta"
        },
        "ortodoncia": {
          "type": "action",
          "actions": [
            { "type": "tag", "name": "interes-ortodoncia" },
            { "type": "set", "field": "tratamiento", "value": "ortodoncia" },
            { "type": "meta", "event": "ViewContent", "once": true }
          ],
          "next": "ortodoncia_info"
        },
        "ortodoncia_info": {
          "type": "message",
          "say": [{ "text": "Tenemos brackets metálicos, brackets estéticos y alineadores transparentes. Los planes empiezan en {{catalogo.ortodoncia_mensualidad}} y la valoración cuesta {{catalogo.valoracion}}, que se descuenta si inicias el tratamiento." }],
          "next": "agendar_pregunta"
        },
        "agendar_pregunta": {
          "type": "message",
          "say": [{ "text": "¿Te ayudo a agendar tu cita?" }],
          "buttons": [
            { "id": "si", "title": "Sí, agendar", "next": "ir_agendar" },
            { "id": "duda", "title": "Tengo una duda", "next": "dudas" }
          ],
          "next": "espera_agendar"
        },
        "espera_agendar": { "type": "wait", "minutes": 120, "next": "recordatorio_agendar" },
        "recordatorio_agendar": {
          "type": "message",
          "say": [{ "text": "Si quieres, te aparto un lugar esta semana. Solo toca «Sí, agendar»." }],
          "next": null
        },
        "ir_agendar": { "type": "goto", "flow": "agendar", "node": null },
        "dudas": { "type": "end", "mode": "free", "reply": true }
      }
    },
    {
      "id": "agendar",
      "name": "Agendar cita",
      "kind": "flow",
      "description": "Pide nombre, tratamiento, horario y correo, y pasa la solicitud a recepción.",
      "entry": { "keywords": [], "welcome": false },
      "start": "con_nombre",
      "nodes": {
        "con_nombre": {
          "type": "condition",
          "rules": [
            { "when": { "kind": "field", "op": "empty", "field": "nombre" }, "next": "nombre" },
            { "when": { "kind": "field", "op": "equals", "field": "nombre_origen", "value": "perfil" }, "next": "nombre" }
          ],
          "else": "con_tratamiento"
        },
        "nombre": {
          "type": "message",
          "say": [{ "text": "Con gusto te ayudo a agendar. ¿Me compartes tu nombre completo?" }],
          "next": "espera_nombre"
        },
        "espera_nombre": { "type": "wait", "minutes": 30, "for": { "fields": ["nombre"] }, "got": "con_tratamiento", "next": "con_tratamiento" },
        "con_tratamiento": {
          "type": "condition",
          "rules": [{ "when": { "kind": "field", "op": "filled", "field": "tratamiento" }, "next": "horario" }],
          "else": "tratamiento"
        },
        "tratamiento": {
          "type": "message",
          "say": [{ "text": "Gracias {{nombre}}. ¿Qué tratamiento te interesa: limpieza, blanqueamiento, ortodoncia o una revisión general?" }],
          "next": "espera_tratamiento"
        },
        "espera_tratamiento": { "type": "wait", "minutes": 60, "for": { "fields": ["tratamiento"] }, "got": "horario", "next": "horario" },
        "horario": {
          "type": "call",
          "component": "elegir-horario",
          "params": { "tratamiento": "{{tratamiento}}" },
          "results": { "elegido": "correo" },
          "next": "sin_horario"
        },
        "correo": {
          "type": "message",
          "say": [{ "text": "¿A qué correo te enviamos la confirmación?" }],
          "next": "espera_correo"
        },
        "espera_correo": { "type": "wait", "minutes": 60, "for": { "fields": ["correo"] }, "got": "solicitud", "next": "solicitud" },
        "solicitud": {
          "type": "action",
          "actions": [
            { "type": "tag", "name": "cita-solicitada" },
            { "type": "meta", "event": "LeadSubmitted" }
          ],
          "next": "resumen"
        },
        "resumen": {
          "type": "message",
          "say": [
            { "text": "Listo {{nombre}}. Tu solicitud quedó así:\n• Tratamiento: {{tratamiento}}\n• Horario: {{horario_preferido}}" },
            { "text": "Recepción te confirma el día y la hora por este medio. Estamos en {{catalogo.direccion}}: {{catalogo.ubicacion}}", "delay": 2 }
          ],
          "next": "es_blanqueamiento"
        },
        "es_blanqueamiento": {
          "type": "condition",
          "rules": [
            { "when": { "kind": "field", "op": "equals", "field": "tratamiento", "value": "blanqueamiento" }, "next": "ir_anticipo" }
          ],
          "else": "fin_persona"
        },
        "ir_anticipo": { "type": "goto", "flow": "anticipo", "node": null },
        "fin_persona": { "type": "end", "mode": "human" },
        "sin_horario": {
          "type": "message",
          "say": [{ "text": "Sin problema. Cuando sepas qué horario te queda, escríbeme y te aparto un lugar." }],
          "next": "fin_libre"
        },
        "fin_libre": { "type": "end", "mode": "free" }
      }
    },
    {
      "id": "elegir-horario",
      "name": "Elegir horario",
      "kind": "component",
      "description": "Pregunta el horario preferido, lo guarda en {{horario_preferido}} y regresa con el resultado «elegido».",
      "params": ["tratamiento"],
      "results": ["elegido"],
      "start": "pregunta",
      "nodes": {
        "pregunta": {
          "type": "message",
          "say": [{ "text": "¿Qué horario te acomoda para tu cita de {{param.tratamiento}}? Atendemos {{catalogo.horario}}." }],
          "buttons": [
            { "id": "manana", "title": "Mañana", "next": "manana" },
            { "id": "tarde", "title": "Tarde", "next": "tarde" },
            { "id": "sabado", "title": "Sábado", "next": "sabado" }
          ],
          "next": "espera"
        },
        "espera": { "type": "wait", "minutes": 120, "next": "recordatorio" },
        "recordatorio": {
          "type": "message",
          "say": [{ "text": "¿Te aparto algún horario? Toca el que prefieras." }],
          "next": "espera_final"
        },
        "espera_final": { "type": "wait", "minutes": 240, "next": "sin_respuesta" },
        "manana": { "type": "action", "actions": [{ "type": "set", "field": "horario_preferido", "value": "mañana" }], "next": "listo" },
        "tarde": { "type": "action", "actions": [{ "type": "set", "field": "horario_preferido", "value": "tarde" }], "next": "listo" },
        "sabado": { "type": "action", "actions": [{ "type": "set", "field": "horario_preferido", "value": "sábado" }], "next": "listo" },
        "listo": { "type": "end", "mode": "return", "result": "elegido" },
        "sin_respuesta": { "type": "end", "mode": "return" }
      }
    },
    {
      "id": "anticipo",
      "name": "Anticipo del blanqueamiento",
      "kind": "flow",
      "description": "Envía el enlace del anticipo y espera a que recepción lo confirme con la etiqueta «anticipo-confirmado».",
      "entry": { "keywords": [], "welcome": false },
      "start": "pendiente",
      "nodes": {
        "pendiente": {
          "type": "action",
          "actions": [
            { "type": "tag", "name": "anticipo-pendiente" },
            { "type": "meta", "event": "InitiateCheckout", "value": "{{catalogo.anticipo_monto}}", "currency": "MXN", "once": true }
          ],
          "next": "enlace"
        },
        "enlace": {
          "type": "message",
          "say": [{ "text": "El blanqueamiento se aparta con un anticipo de {{catalogo.anticipo}}, que se descuenta del precio. Puedes pagarlo aquí: {{catalogo.enlace_anticipo}}\nEn cuanto recepción lo confirme, te aviso por este medio." }],
          "next": "espera_pago"
        },
        "espera_pago": { "type": "wait", "minutes": 180, "for": { "tag": "anticipo-confirmado" }, "got": "confirmado", "next": "pregunta_pago" },
        "confirmado": {
          "type": "action",
          "actions": [
            { "type": "untag", "name": "anticipo-pendiente" },
            { "type": "meta", "event": "Purchase", "value": "{{catalogo.anticipo_monto}}", "currency": "MXN" }
          ],
          "next": "gracias"
        },
        "gracias": {
          "type": "message",
          "say": [{ "text": "¡Gracias {{nombre}}! Recibimos tu anticipo. Recepción te confirma el día y la hora de tu blanqueamiento por aquí." }],
          "next": "fin_persona"
        },
        "pregunta_pago": {
          "type": "message",
          "say": [{ "text": "¿Pudiste pagar el anticipo? Si el enlace te dio algún problema, dime y te ayudo." }],
          "buttons": [
            { "id": "ya", "title": "Ya pagué", "next": "revisar" },
            { "id": "ayuda", "title": "Necesito ayuda", "next": "ayuda" }
          ],
          "next": "espera_respuesta"
        },
        "espera_respuesta": { "type": "wait", "minutes": 600, "for": { "tag": "anticipo-confirmado" }, "got": "confirmado", "next": "sin_pago" },
        "revisar": { "type": "action", "actions": [{ "type": "tag", "name": "revisar-pago" }], "next": "revision" },
        "revision": {
          "type": "message",
          "say": [{ "text": "Gracias. Recepción revisa tu pago y te confirma por aquí." }],
          "next": "fin_persona"
        },
        "ayuda": { "type": "end", "mode": "free", "reply": true },
        "sin_pago": { "type": "end", "mode": "free" },
        "fin_persona": { "type": "end", "mode": "human" }
      }
    },
    {
      "id": "persona",
      "name": "Pasar a una persona",
      "kind": "flow",
      "description": "Avisa al contacto y deja el chat con el equipo. Lo usa la intención «asesor».",
      "entry": { "keywords": [], "welcome": false },
      "start": "aviso",
      "nodes": {
        "aviso": {
          "type": "message",
          "say": [{ "text": "Con gusto. En unos minutos te escribe una persona del equipo." }],
          "next": "fin"
        },
        "fin": { "type": "end", "mode": "human" }
      }
    },
    {
      "id": "baja",
      "name": "Ya no le interesa",
      "kind": "flow",
      "description": "Se despide sin insistir. Lo usa la intención «no_interesa».",
      "entry": { "keywords": [], "welcome": false },
      "start": "adios",
      "nodes": {
        "adios": {
          "type": "message",
          "say": [{ "text": "Entendido, gracias por avisarnos. Si más adelante necesitas algo, aquí estamos." }],
          "next": "fin"
        },
        "fin": { "type": "end", "mode": "silent" }
      }
    }
  ]
}
```

Cómo se comporta:

| El contacto… | Qué pasa |
|---|---|
| Escribe por primera vez «Hola, buenas tardes» | Entra a la bienvenida: menú con tres botones. Si no contesta, a la hora recibe el recordatorio; 5 horas después, el horario y el flujo termina. Si el recordatorio vence fuera de horario (después de las 19:00), sale al abrir. |
| Toca «Limpieza dental» el 16 de noviembre | Etiqueta `interes-limpieza`, `{{tratamiento}}` = «limpieza», `ViewContent` a Meta, el precio de promoción y «¿Te ayudo a agendar tu cita?» con dos botones. Después del 30 de noviembre, el precio normal. |
| Pregunta «¿cuánto cuesta el blanqueamiento?» con esos botones en pantalla | Ningún botón ofrece ese precio, así que aplica la intención `precios`, aunque haya un flujo en curso: la IA contesta siguiendo su instrucción (el precio del catálogo y ofrecer una valoración), se guarda «blanqueamiento» como tratamiento y los botones siguen esperando. |
| Toca «Sí, agendar» | Pasa a `agendar`: pide el nombre. Al darlo, como ya sabe el tratamiento, la Condición `con_tratamiento` salta esa pregunta y pasa al componente: «¿Qué horario te acomoda para tu cita de limpieza?». |
| Pregunta «¿duele la limpieza?» mientras elige horario | La IA contesta con la pregunta frecuente; los botones de horario siguen vigentes y el recordatorio cuenta desde ese mensaje. |
| Escribe «mejor en la tarde» | El intérprete lo reconoce como el botón «Tarde»: guarda el horario y pide el correo. |
| Escribe sin menú «Quiero agendar, soy Luis, me interesa una limpieza» | Se guardan el nombre y el tratamiento; la intención `agendar` lo lleva a `agendar`, donde las Condiciones saltan las preguntas del nombre y del tratamiento y va directo al horario. |
| Da su correo después de elegir blanqueamiento | Resumen, etiqueta `cita-solicitada`, `LeadSubmitted` y el enlace del anticipo. Cuando recepción pone `anticipo-confirmado`, recibe la confirmación, sale `Purchase` y el chat pasa al equipo. |
| Mientras espera el anticipo, recepción pasa el chat a Persona, le contesta, pone `anticipo-confirmado` y lo devuelve al bot | El flujo sigue donde estaba: como ya tiene la etiqueta, sale por «Se cumplió», recibe la confirmación, sale `Purchase` y el chat pasa al equipo. |
| No contesta «¿Te ayudo a agendar tu cita?» y al día siguiente escribe «quiero agendar» | A las 2 horas recibe el recordatorio y, como no hay nada conectado después, el flujo termina. Al día siguiente la conversación está libre: su mensaje entra a `agendar`, por la intención `agendar` o porque el intérprete lo reconoce como el botón «Sí, agendar» (los botones del último menú sirven para esa respuesta). |
| Dice «se me rompió un diente» en cualquier momento | `urgencia` es urgente: va antes que todo, aunque el mismo mensaje también toque un botón o dé el dato que se espera. Etiqueta `urgencia`, la IA avisa siguiendo su instrucción (con el teléfono del catálogo) y el chat pasa al equipo. Si estaba en un flujo (por ejemplo, el menú de la bienvenida), ese flujo queda en pausa y, cuando recepción devuelve el chat al bot, sigue donde iba: el Esperar del menú vuelve a contar completo. |
| Pide «¿me puede atender alguien?» | `asesor` lleva al flujo `persona`: envía el aviso y el chat pasa al equipo. Si estaba en otro flujo, ese flujo queda suspendido y sigue donde iba cuando recepción devuelve el chat al bot. |
| Está viendo el menú de la bienvenida y escribe «¿dónde están?» | Ningún botón lo ofrece y no hay una intención para eso: la IA contesta con la dirección del catálogo y el menú sigue esperando. |
| Está en la bienvenida y escribe «ya no me escriban» | `no_interesa` lleva a `baja`: se despide y termina con `silent`, así que el menú de la bienvenida no sigue y no hay seguimiento automático. |
| Escribe «ok, gracias» mientras espera la confirmación del anticipo | La IA contesta con algo muy breve y el Esperar sigue. |
