Skip to main content
POST
/
messages
/
v1
curl -X POST https://api.whaapy.com/messages/v1 \
  -H "Authorization: Bearer wha_TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+5215512345678",
    "type": "interactive",
    "interactive": {
      "type": "button",
      "body": {
        "text": "¿Qué te gustaría hacer hoy?"
      },
      "action": {
        "buttons": [
          {
            "type": "reply",
            "reply": {
              "id": "ver_menu",
              "title": "Ver Menú"
            }
          },
          {
            "type": "reply",
            "reply": {
              "id": "hacer_pedido",
              "title": "Hacer Pedido"
            }
          },
          {
            "type": "reply",
            "reply": {
              "id": "hablar_agente",
              "title": "Hablar con Agente"
            }
          }
        ]
      }
    }
  }'
{
  "messaging_product": "whatsapp",
  "contacts": [
    { "input": "+5215512345678", "wa_id": "5215512345678" }
  ],
  "messages": [
    { "id": "550e8400-e29b-41d4-a716-446655440000", "wamid": "wamid.HBgLNTIxNTUx..." }
  ],
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "conversationId": "660e8400-e29b-41d4-a716-446655440001",
    "messageType": "interactive",
    "direction": "outbound",
    "status": "sent",
    "createdAt": "2026-01-22T10:30:00Z"
  }
}
Los mensajes interactivos permiten a los usuarios responder con un tap en lugar de escribir. Whaapy soporta en esta fase:
  • Botones: Hasta 3 opciones rápidas
  • Listas: Menús desplegables con secciones
  • Flows: Apertura de un Flow de WhatsApp ya creado en Meta
Los mensajes interactivos solo pueden enviarse dentro de la ventana de 24 horas. Fuera de la ventana, usa templates.

Botones (Reply Buttons)

Muestra hasta 3 botones con opciones rápidas. Ideal para preguntas simples o menús pequeños.

Ejemplo Básico

curl -X POST https://api.whaapy.com/messages/v1 \
  -H "Authorization: Bearer wha_TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+5215512345678",
    "type": "interactive",
    "interactive": {
      "type": "button",
      "body": {
        "text": "¿Qué te gustaría hacer hoy?"
      },
      "action": {
        "buttons": [
          {
            "type": "reply",
            "reply": {
              "id": "ver_menu",
              "title": "Ver Menú"
            }
          },
          {
            "type": "reply",
            "reply": {
              "id": "hacer_pedido",
              "title": "Hacer Pedido"
            }
          },
          {
            "type": "reply",
            "reply": {
              "id": "hablar_agente",
              "title": "Hablar con Agente"
            }
          }
        ]
      }
    }
  }'
{
  "to": "+5215512345678",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "header": {
      "type": "text",
      "text": "🍕 Pizzería Whaapy"
    },
    "body": {
      "text": "¡Bienvenido! ¿En qué podemos ayudarte?"
    },
    "footer": {
      "text": "Responde con un botón"
    },
    "action": {
      "buttons": [
        {
          "type": "reply",
          "reply": { "id": "menu", "title": "Ver Menú" }
        },
        {
          "type": "reply",
          "reply": { "id": "pedido", "title": "Mi Pedido" }
        }
      ]
    }
  }
}

Con Header de Imagen

{
  "to": "+5215512345678",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "header": {
      "type": "image",
      "image": {
        "link": "https://example.com/promo.jpg"
      }
    },
    "body": {
      "text": "¡Promoción especial! 2x1 en pizzas familiares."
    },
    "action": {
      "buttons": [
        {
          "type": "reply",
          "reply": { "id": "aprovechar", "title": "¡Quiero!" }
        },
        {
          "type": "reply",
          "reply": { "id": "mas_info", "title": "Más Info" }
        }
      ]
    }
  }
}

Campos de Botones

interactive.type
string
required
Debe ser "button" para reply buttons.
interactive.header
object
Header opcional. Puede ser text, image, video, o document.
interactive.body.text
string
required
Texto principal del mensaje. Máximo 1024 caracteres.
Texto secundario en gris. Máximo 60 caracteres.
interactive.action.buttons
array
required
Array de 1-3 botones. Cada botón tiene type: "reply" y objeto reply con id y title.
button.reply.id
string
required
ID único del botón. Lo recibirás en el webhook cuando el usuario responda. Máximo 256 caracteres.
button.reply.title
string
required
Texto visible del botón. Máximo 20 caracteres.

Listas (List Messages)

Menús desplegables con hasta 10 opciones organizadas en secciones. Ideal para catálogos, horarios, o menús extensos.

Ejemplo Básico

{
  "to": "+5215512345678",
  "type": "interactive",
  "interactive": {
    "type": "list",
    "body": {
      "text": "Elige una opción de nuestro menú:"
    },
    "action": {
      "button": "Ver Opciones",
      "sections": [
        {
          "title": "Pizzas",
          "rows": [
            {
              "id": "pizza_margarita",
              "title": "Margarita",
              "description": "Tomate, mozzarella, albahaca - $150"
            },
            {
              "id": "pizza_pepperoni",
              "title": "Pepperoni",
              "description": "Pepperoni, mozzarella - $180"
            }
          ]
        },
        {
          "title": "Bebidas",
          "rows": [
            {
              "id": "bebida_refresco",
              "title": "Refresco",
              "description": "Coca-Cola, Sprite, Fanta - $35"
            },
            {
              "id": "bebida_agua",
              "title": "Agua",
              "description": "Natural o mineral - $25"
            }
          ]
        }
      ]
    }
  }
}
{
  "to": "+5215512345678",
  "type": "interactive",
  "interactive": {
    "type": "list",
    "header": {
      "type": "text",
      "text": "🍕 Menú Pizzería Whaapy"
    },
    "body": {
      "text": "Tenemos las mejores pizzas de la ciudad. ¡Elige tu favorita!"
    },
    "footer": {
      "text": "Envío gratis en pedidos mayores a $300"
    },
    "action": {
      "button": "Ver Menú",
      "sections": [
        {
          "title": "Pizzas Clásicas",
          "rows": [
            {
              "id": "margarita",
              "title": "Margarita",
              "description": "Tomate, mozzarella, albahaca fresca"
            },
            {
              "id": "pepperoni",
              "title": "Pepperoni",
              "description": "Pepperoni premium, mozzarella"
            },
            {
              "id": "hawaiana",
              "title": "Hawaiana",
              "description": "Jamón, piña, mozzarella"
            }
          ]
        },
        {
          "title": "Pizzas Especiales",
          "rows": [
            {
              "id": "4_quesos",
              "title": "4 Quesos",
              "description": "Mozzarella, parmesano, gorgonzola, provolone"
            },
            {
              "id": "bbq_chicken",
              "title": "BBQ Chicken",
              "description": "Pollo, salsa BBQ, cebolla caramelizada"
            }
          ]
        }
      ]
    }
  }
}

Campos de Listas

interactive.type
string
required
Debe ser "list" para list messages.
interactive.header
object
Header opcional. Solo soporta type: "text" en listas.
interactive.body.text
string
required
Texto principal. Máximo 1024 caracteres.
Texto secundario. Máximo 60 caracteres.
interactive.action.button
string
required
Texto del botón que abre la lista. Máximo 20 caracteres.
interactive.action.sections
array
required
Array de secciones. Máximo 10 secciones, máximo 10 rows en total.

Campos de Section

section.title
string
Título de la sección. Máximo 24 caracteres. Requerido si hay más de una sección.
section.rows
array
required
Array de opciones dentro de la sección.

Campos de Row

row.id
string
required
ID único de la opción. Lo recibirás en el webhook. Máximo 200 caracteres.
row.title
string
required
Título visible de la opción. Máximo 24 caracteres.
row.description
string
Descripción adicional. Máximo 72 caracteres.

Respuesta del Usuario

Cuando el usuario selecciona un botón o item de lista, recibirás un webhook con el id seleccionado:
Webhook - Botón seleccionado
{
  "type": "interactive",
  "interactive": {
    "type": "button_reply",
    "button_reply": {
      "id": "hacer_pedido",
      "title": "Hacer Pedido"
    }
  }
}
Webhook - Lista seleccionada
{
  "type": "interactive",
  "interactive": {
    "type": "list_reply",
    "list_reply": {
      "id": "pizza_pepperoni",
      "title": "Pepperoni",
      "description": "Pepperoni, mozzarella - $180"
    }
  }
}
Webhook - Flow enviado por el usuario
{
  "type": "interactive",
  "interactive": {
    "type": "nfm_reply",
    "nfm_reply": {
      "name": "booking_flow",
      "body": "Sucursal Centro · 2026-03-10 16:00",
      "response_json": "{\"screen\":\"BOOK_APPOINTMENT\",\"values\":{\"branch\":\"Sucursal Centro\",\"slot\":\"2026-03-10 16:00\",\"service\":\"demo\"}}"
    }
  }
}
Usa IDs descriptivos como "confirmar_pedido_12345" para facilitar el procesamiento en tu backend.

Respuesta Exitosa

{
  "messaging_product": "whatsapp",
  "contacts": [
    { "input": "+5215512345678", "wa_id": "5215512345678" }
  ],
  "messages": [
    { "id": "550e8400-e29b-41d4-a716-446655440000", "wamid": "wamid.HBgLNTIxNTUx..." }
  ],
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "conversationId": "660e8400-e29b-41d4-a716-446655440001",
    "messageType": "interactive",
    "direction": "outbound",
    "status": "sent",
    "createdAt": "2026-01-22T10:30:00Z"
  }
}

Límites

ElementoLímite
Botones por mensaje3
Secciones en lista10
Rows totales en lista10
Caracteres en body1024
Caracteres en título de botón20
Caracteres en título de row24
Caracteres en descripción de row72
Caracteres en footer60

Comparación: Botones vs Listas

AspectoBotonesListas
Opciones máximas310
VisibilidadSiempre visiblesRequiere tap para expandir
Header con imagenNo (solo texto)
DescripcionesNo
OrganizaciónFlatSecciones
Mejor paraDecisiones rápidasCatálogos, menús

Casos de Uso

{
  "type": "button",
  "body": { "text": "Tu pedido #12345 está listo. ¿Confirmamos envío?" },
  "action": {
    "buttons": [
      { "type": "reply", "reply": { "id": "confirmar", "title": "Confirmar" } },
      { "type": "reply", "reply": { "id": "modificar", "title": "Modificar" } },
      { "type": "reply", "reply": { "id": "cancelar", "title": "Cancelar" } }
    ]
  }
}
{
  "type": "list",
  "body": { "text": "Selecciona tu horario de entrega preferido:" },
  "action": {
    "button": "Ver Horarios",
    "sections": [
      {
        "title": "Mañana",
        "rows": [
          { "id": "9am", "title": "9:00 - 11:00 AM" },
          { "id": "11am", "title": "11:00 - 1:00 PM" }
        ]
      },
      {
        "title": "Tarde",
        "rows": [
          { "id": "2pm", "title": "2:00 - 4:00 PM" },
          { "id": "4pm", "title": "4:00 - 6:00 PM" }
        ]
      }
    ]
  }
}
{
  "type": "button",
  "body": { "text": "¿Cómo calificarías tu experiencia con nosotros?" },
  "action": {
    "buttons": [
      { "type": "reply", "reply": { "id": "excelente", "title": "😊 Excelente" } },
      { "type": "reply", "reply": { "id": "buena", "title": "🙂 Buena" } },
      { "type": "reply", "reply": { "id": "mejorar", "title": "😕 Puede mejorar" } }
    ]
  }
}

Flows de WhatsApp

Usa un Flow ya existente en Meta sin necesidad de crearlo desde Whaapy. El contrato sigue el formato nativo de WhatsApp Cloud API: type: "interactive" con interactive.type: "flow".

Ejemplo Básico

{
  "to": "+5215512345678",
  "type": "interactive",
  "interactive": {
    "type": "flow",
    "header": {
      "type": "text",
      "text": "Reserva tu cita"
    },
    "body": {
      "text": "Completa este flujo para reservar tu cita en menos de 1 minuto."
    },
    "footer": {
      "text": "Datos protegidos"
    },
    "action": {
      "name": "flow",
      "parameters": {
        "flow_message_version": "3",
        "flow_id": "123456789012345",
        "flow_cta": "Abrir flow",
        "flow_token": "lead_987654321",
        "flow_action": "navigate",
        "flow_action_payload": {
          "screen": "BOOK_APPOINTMENT",
          "data": {
            "service": "demo",
            "source": "api"
          }
        }
      }
    }
  }
}

Campos de Flow

interactive.type
string
required
Debe ser "flow".
interactive.action.name
string
required
Debe ser "flow".
interactive.action.parameters.flow_message_version
string
required
Actualmente debe ser "3".
interactive.action.parameters.flow_id
string
ID único del Flow en Meta. Debes enviar flow_id o flow_name, pero no ambos.
interactive.action.parameters.flow_name
string
Nombre del Flow en Meta. Alternativa a flow_id.
interactive.action.parameters.flow_cta
string
required
Texto del CTA visible para abrir el Flow. Recomendado: 30 caracteres o menos.
interactive.action.parameters.flow_token
string
Token propio de tu negocio para correlacionar la respuesta del Flow.
interactive.action.parameters.flow_action
string
navigate o data_exchange. Si no lo envías, Meta usa navigate.
interactive.action.parameters.flow_action_payload.screen
string
Pantalla inicial del Flow cuando usas navigate.
interactive.action.parameters.flow_action_payload.data
object
Datos iniciales que quieres inyectar a la primera pantalla del Flow.

Próximos Pasos