Saltar al contenido
Chatbots de WhatsApp desde cero
Temario Ir al ejercicio →

Formularios dentro de WhatsApp con Flows

  • ⏱ 60 minutos
  • 📶 Nivel: avanzado
  • 🧩 Incluye ejercicio

💡 🎯 Al terminar esta lección podrás…

  • Explicar qué es un Flow y en qué se diferencia de botones, listas y texto libre.
  • Leer y escribir el JSON de un Flow de dos pantallas, con campos, navigate y complete.
  • Construir el mensaje que abre un Flow y leer la respuesta (nfm_reply) que llega al webhook.
  • Distinguir un Flow sin endpoint de uno con endpoint y saber qué exige este último (cifrado, respuestas rápidas).
  • Decidir con criterio cuándo usar un Flow y cuándo basta con botones o texto.

⚠️ INFO REFERENCIAL: revisa los sitios oficiales

Los precios, planes, límites y tarifas de esta lección son solo una referencia (revisados en octubre de 2026) y cambian con frecuencia. Antes de decidir o cobrar a un cliente, confirma siempre en los sitios oficiales de cada proveedor (enlaces al final de la lección).

1.El problema: pedir tres datos por chat es incómodo

En la lección 8 el bot pide los datos del delivery uno por uno: «¿Zona?», «¿Dirección?», «¿Referencia?». Funciona, pero tiene un costo real: cada pregunta es un mensaje, el cliente puede responder fuera de orden, escribir la dirección en el campo de la zona o abandonar a la mitad. Y si el bot necesita seis datos, la conversación se vuelve un interrogatorio.

Un WhatsApp Flow resuelve justo eso: es un formulario con pantallas que se abre dentro del chat, con campos de texto, listas desplegables, casillas, selectores de fecha, y un botón de enviar. El cliente lo llena de una vez, ve los errores en el mismo formulario y, al terminar, tu bot recibe todos los datos juntos en un solo mensaje.

Botenvía el mensaje con Flow
→botón "Pedir delivery"
Clientellena el formulario
→enviar
Webhookrecibe nfm_reply
→datos juntos
Botcontinúa el pedido

Un Flow no reemplaza al bot: es una herramienta más, como los botones. Útil sobre todo para reservas, solicitudes de cotización, encuestas y pedidos con varios datos.

⚠️ Importante · Verifica este dato

Los Flows evolucionan rápido: aparecen componentes nuevos, cambian las versiones del JSON y los requisitos de la cuenta. Todo lo de esta lección se contrastó con la documentación oficial de Flows el 2026-10-07. Antes de construir, confirma la versión actual del Flow JSON y los requisitos de tu cuenta en esa documentación.

2.Flows frente a botones, listas y texto libre

Ya conoces tres formas de interactuar. Este es el mapa:

Herramienta Ideal para Límite
Texto libre Que el cliente diga lo que quiere con sus palabras Hay que interpretarlo (lección 6)
Botones de respuesta Elegir entre 1 y 3 opciones Máximo 3 botones
Lista Elegir entre hasta 10 opciones Una sola elección por mensaje
Flow Reunir varios datos con validación Más trabajo: diseñar, probar, mantener

Una regla práctica para decidir:

En el ejercicio escribirás esa regla como una función. No te enamores del Flow por ser vistoso: un bot sencillo que funciona bate a un Flow elaborado que nadie mantiene.

3.Las dos formas de Flow: sin endpoint y con endpoint

Esta es la decisión técnica más importante.

Flow sin endpoint. Todo el comportamiento vive en el JSON del Flow. Las pantallas se navegan solas, los campos se validan con lo que ofrece el propio formulario (campo obligatorio, tipo de entrada) y al terminar, WhatsApp te manda los datos por el webhook. No tienes que programar un servidor extra. Es suficiente para el 80 % de los formularios.

Flow con endpoint. Cada vez que el cliente pasa de pantalla, WhatsApp llama a una URL tuya (el endpoint) para preguntar «¿qué muestro ahora?». Sirve cuando las pantallas dependen de datos dinámicos: horarios disponibles hoy, stock, cobertura de una dirección. A cambio exige:

Flowpantalla DATOS
→data_exchange
Tu endpointvalida la zona
→siguiente pantalla
Flowpantalla RESUMEN

📌 Nota · Empieza sin endpoint

En el curso construimos el Flow sin endpoint: aprendes el formato sin la complejidad del cifrado. Cuando tengas un caso real que lo necesite, lee la guía oficial de implementación del endpoint y reserva una jornada para pruebas. El código de referencia del curso no incluye un endpoint de Flows.

4.El JSON de un Flow

Un Flow se describe con un JSON: una lista de pantallas y, dentro de cada una, una lista de componentes. Este es el Flow de «datos de delivery» del minimarket, ejecutado y validado con la función que escribirás en el ejercicio:

flow-delivery.json
{
  "version": "5.1",
  "screens": [
    {
      "id": "DATOS",
      "title": "Datos de entrega",
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          { "type": "TextHeading", "text": "Dónde te lo llevamos" },
          { "type": "Dropdown", "name": "zona", "label": "Zona", "required": true,
            "data-source": [
              { "id": "Urb. Los Olivos", "title": "Urb. Los Olivos" },
              { "id": "Urb. Las Palmeras", "title": "Urb. Las Palmeras" },
              { "id": "Centro", "title": "Centro" }
            ] },
          { "type": "TextInput", "name": "direccion", "label": "Dirección", "input-type": "text", "required": true },
          { "type": "TextInput", "name": "referencia", "label": "Referencia", "input-type": "text", "required": false },
          { "type": "Footer", "label": "Continuar",
            "on-click-action": {
              "name": "navigate",
              "next": { "type": "screen", "name": "RESUMEN" },
              "payload": { "zona": "${form.zona}", "direccion": "${form.direccion}", "referencia": "${form.referencia}" }
            } }
        ]
      }
    },
    {
      "id": "RESUMEN",
      "title": "Confirma",
      "terminal": true,
      "success": true,
      "data": {
        "zona": { "type": "string", "__example__": "Centro" },
        "direccion": { "type": "string", "__example__": "Av. Ejemplo 123" },
        "referencia": { "type": "string", "__example__": "Frente al parque" }
      },
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          { "type": "TextBody", "text": "Entregamos en ${data.zona}: ${data.direccion}" },
          { "type": "Footer", "label": "Enviar datos",
            "on-click-action": {
              "name": "complete",
              "payload": { "zona": "${data.zona}", "direccion": "${data.direccion}", "referencia": "${data.referencia}" }
            } }
        ]
      }
    }
  ]
}

Lectura guiada:

⚠️ Importante · Verifica este dato

Los nombres exactos de las propiedades de cada componente (data-source, input-type, __example__, etc.) deben contrastarse con la referencia de componentes de Meta. Pega el JSON en el Flow Builder de WhatsApp Manager: te marcará con claridad qué propiedad no acepta tu versión.

Validar el JSON antes de subirlo

Un error típico: una pantalla terminal sin Footer, o un navigate hacia un id mal escrito. Subir el Flow solo para descubrirlo es lento. Con una función de unas veinte líneas (la validarFlow de tu ejercicio) lo detectas en local. Resultado real al validar el Flow anterior y una versión con dos errores introducidos a propósito:

Salida esperada
[]
["navigate hacia una pantalla inexistente: RESUMN","La pantalla terminal RESUMEN no tiene Footer"]

El primer arreglo vacío significa «sin errores». El segundo muestra los dos fallos que se inyectaron: un id mal escrito (RESUMN) y la eliminación del Footer final. Esta validación no sustituye al Flow Builder (que valida las reglas oficiales completas), solo te ahorra viajes.

5.Enviar el Flow al cliente

El Flow se envía como un mensaje interactivo de tipo flow. Es un mensaje normal de la API, así que solo puedes enviarlo dentro de la ventana de 24 horas (como los botones) o dentro de una plantilla si quieres abrirlo fuera de ella. Para el caso del minimarket el cliente ya está conversando, así que usamos el mensaje libre. Este es el cuerpo que genera la función armarMensajeFlow del ejercicio:

mensaje-flow.json
{
  "messaging_product": "whatsapp",
  "recipient_type": "individual",
  "to": "51999000111",
  "type": "interactive",
  "interactive": {
    "type": "flow",
    "body": { "text": "Completa tus datos de entrega." },
    "action": {
      "name": "flow",
      "parameters": {
        "flow_message_version": "3",
        "flow_token": "tok-ejemplo-9f3a",
        "flow_id": "123456",
        "flow_cta": "Pedir delivery",
        "flow_action": "navigate",
        "flow_action_payload": { "screen": "DATOS" }
      }
    }
  }
}

Los parámetros que importan, según la guía de envío de Flows:

Parámetro Para qué sirve
flow_message_version Debe ser "3"
flow_id (o flow_name) El Flow que se abre; usa uno de los dos, no ambos
flow_token Un identificador que generas tú para reconocer esta conversación cuando vuelva la respuesta
flow_cta Texto del botón que abre el formulario; la documentación aconseja 30 caracteres o menos y sin emojis
flow_action navigate (abre una pantalla) o data_exchange (consulta primero tu endpoint)
flow_action_payload.screen El id de la primera pantalla
mode draft para probar el borrador, published (por omisión) para el Flow publicado

El flow_token merece atención. Piensa en él como un ticket: lo generas cuando envías el Flow (por ejemplo, ligado al teléfono y al pedido en curso, con una parte aleatoria) y lo guardas. Cuando llegue la respuesta, lo comparas: si no coincide, ignoras el mensaje. Así evitas que alguien rellene un Flow viejo o ajeno. Más sobre este tipo de defensas en la lección 24.

⚠️ Importante · Verifica este dato

Para enviar Flows la documentación menciona prerequisitos de la cuenta (verificación del negocio y calidad de mensajes) y que un Flow debe estar publicado para usarse en producción. Confírmalos en la guía de envío. Mientras practicas, mode: "draft" permite probar el borrador.

6.La respuesta: nfm_reply en tu webhook

Cuando el cliente toca «Enviar datos», tu webhook recibe un mensaje entrante interactivo distinto a los de botones y listas. Su interactive.type es nfm_reply y los datos llegan en response_json, como texto (un JSON dentro de un JSON). Un ejemplo:

JSON
{
  "from": "51999000111",
  "id": "wamid.X",
  "timestamp": "1790000000",
  "type": "interactive",
  "interactive": {
    "type": "nfm_reply",
    "nfm_reply": {
      "name": "flow",
      "body": "Sent",
      "response_json": "{\"flow_token\":\"tok-ejemplo-9f3a\",\"zona\":\"Centro\",\"direccion\":\"Av. Ejemplo 123\",\"referencia\":\"Frente al parque\"}"
    }
  }
}

⚠️ Importante · Verifica este dato

La estructura exacta de nfm_reply y de response_json no estaba en las páginas de documentación que se pudieron consultar para escribir esta lección. El ejemplo sigue el formato más habitual en la práctica. Confírmalo con la documentación de webhooks de Flows y con un envío real de prueba.

¿Qué hace hoy el bot de referencia con eso?

Esta es la parte honesta: el parsearWebhook del repositorio no conoce nfm_reply. Lo trata como «no soportado». Lo comprobé con el mensaje anterior:

Salida esperada
{"tipo":"no_soportado","subtipo":"interactive:nfm_reply"}

Eso es lo correcto por diseño (el bot no se rompe ante algo desconocido), pero significa que, para usar Flows, tienes que añadir soporte. La idea es convertir el mensaje en una entrada nueva del motor. Esta adaptación, ejecutada con el mismo mensaje, produce:

adaptar-flow.mjs
function adaptar(evento, msgCrudo) {
  if (evento.entrada.subtipo !== 'interactive:nfm_reply') return evento;
  const datos = JSON.parse(msgCrudo.interactive.nfm_reply.response_json);
  return { ...evento, entrada: { tipo: 'flow', datos } };
}
Salida esperada
{"tipo":"flow","datos":{"flow_token":"tok-ejemplo-9f3a","zona":"Centro","direccion":"Av. Ejemplo 123"}}

En tu proyecto lo harías dentro de parsearEntrada (en whatsapp.js) y con try/catch: el cliente controla ese JSON, así que puede llegar roto. La función leerRespuestaFlow del ejercicio ya hace eso y devuelve null si algo no cuadra.

7.Validar siempre en tu servidor

Hay una regla que debes grabarte: los datos de un Flow son entrada del usuario, igual que un texto escrito. Que el formulario marque «obligatorio» no significa que el dato sea válido: un cliente malicioso puede enviar un response_json armado a mano. Por eso, al recibir la respuesta:

  1. Comprueba el flow_token contra el que guardaste.
  2. Valida cada campo como si viniera de texto libre.
  3. Si algo falla, vuelve a pedir ese dato con un mensaje normal.

En el minimarket eso es validarDatosDelivery: la zona debe estar en NEGOCIO.zonas y la dirección debe tener al menos 8 caracteres (la misma regla que ya aplica el motor). Resultado real con las zonas ficticias del curso:

Salida esperada
{"ok":true}
{"ok":false,"error":"Esa zona no tiene reparto."}
{"ok":false,"error":"La direccion es muy corta."}

El orden de las comprobaciones es: token, zonas, dirección, y solo entonces el siguiente paso del pedido (pago, confirmación). Si alguna falla, el bot responde en la conversación con un texto claro, sin emojis, y sin perder el carrito.

8.Cómo encaja un Flow en el motor del minimarket

El motor de reglas es una máquina de estados (lección 5). Un Flow es un estado más con su propia entrada. El recorrido de delivery quedaría así:

  1. El cliente elige «Delivery». El motor pasa al estado DIRECCION y responde con el mensaje de Flow (en vez de preguntar «¿cuál es tu dirección?»).
  2. El cliente llena el formulario. Llega nfm_reply → entrada { tipo: 'flow', datos }.
  3. El motor valida (validarDatosDelivery). Si es válido, guarda entrega: 'delivery', direccion y pasa a PAGO. Si no, vuelve a pedir ese dato.
  4. Plan B: si el cliente nunca abre el Flow o prefiere escribir, el bot debe aceptar también la dirección como texto libre. No todos los clientes (ni todas las versiones de WhatsApp) soportan Flows igual de bien.

Ese plan B es una regla de diseño: todo Flow necesita una ruta alternativa con botones o texto. Un bot cuyo único camino es un formulario falla justo cuando alguien lo necesita.

9.Cuánto cuesta y qué mantener

Tres puntos que condicionan la decisión de usar Flows en un proyecto real:

🧪 Ejemplo · Cuándo SÍ y cuándo NO en el minimarket

Sí: pedido de delivery con zona, dirección, referencia y hora de entrega (cuatro datos relacionados). Sí: encuesta de satisfacción de cuatro preguntas. No: «¿recojo o delivery?» (un botón basta). No: «¿efectivo, Yape o transferencia?» (tres botones). No: consultar el horario (un texto).

10.Errores frecuentes

11.Apuntes para llevar

12.Glosario

Término Significado
Flow Formulario con pantallas que se abre dentro de una conversación de WhatsApp.
Flow JSON Descripción en JSON de las pantallas y componentes de un Flow.
Endpoint URL de tu servidor a la que el Flow consulta datos pantalla a pantalla (opcional).
navigate Acción que lleva a otra pantalla del Flow pasando datos.
complete Acción que cierra el Flow y envía las respuestas al webhook.
flow_token Identificador que generas tú para reconocer a qué conversación pertenece una respuesta.
nfm_reply Tipo de mensaje entrante que contiene las respuestas del Flow.
response_json Texto JSON con los valores que llenó el cliente.

🧠 Comprueba lo aprendido

Elige una respuesta en cada pregunta. Verás al instante si acertaste y por qué.

¿Cuándo conviene más un Flow que unas preguntas sueltas con botones?

¿Cómo llegan al webhook los datos que el cliente llenó en un Flow?

¿Qué diferencia a un Flow con endpoint de uno sin endpoint?

¿Para qué sirve el flow_token?

Un campo marcado required: true en el Flow, ¿te exime de validar el dato en tu servidor?

¿Qué debe tener todo bot que use un Flow además del formulario?

📚 Para profundizar (fuentes oficiales)

Este material lo hace un estudiante con ayuda de IA: ante cualquier duda, manda la documentación oficial.

Tu progreso se guarda en este navegador y se refleja en el índice del curso.

💡 🚀 ¡Ahora te toca!

La práctica fija lo aprendido. Pasa al ejercicio de esta lección.

Ir al ejercicio