WhatsApp Business API para catálogo de productos — lista interactiva
Este escenario envía una lista interactiva con sendList durante la ventana de sesión de 24 horas.
Descripción del caso de uso
Este escenario envía una lista interactiva con sendList durante la ventana de sesión de 24 horas. Dos secciones agrupan filas de productos; el cliente abre el catálogo y selecciona un artículo.
Ejemplo de plantilla
Explora nuestro catálogo para {{1}}:
- {{1}}contexto de campaña o producto mostrado en el texto del encabezado de la lista

Cuándo usarlo
Usa este escenario cuando una campaña de venta en WhatsApp ya tiene sesión abierta y el comprador debe elegir un SKU sin que un agente pegue listas largas de productos. Encaja en equipos que lanzan promociones donde un catálogo estructurado supera al texto libre y el ingreso manual de SKU frenaría el checkout.
Flujo de trabajo
- El cliente actúa
Un evento de campaña o respuesta del cliente mantiene la sesión abierta.
acción del usuario - Capturar evento
Tu backend construye una lista interactiva de dos secciones con filas de productos.
POST/send_list - El cliente toca Ver
El cliente toca Ver catálogo y selecciona un producto.
acción del usuario - Entregado
El webhook devuelve el id de fila para procesamiento de pedido o CRM.
entregado

Implementación técnica
Requisitos previos
- Clave API de 1MSG · Cómo obtener la clave API
- Cuenta de WhatsApp Business · Cómo conectar WABA
- Ventana de sesión de 24 horas abierta · Cómo funciona la ventana de 24 horas
- Opt-in del cliente · Cómo gestionar el consentimiento
- Endpoint de webhook · Cómo configurar webhooks
Ejemplos de código
#!/usr/bin/env bash
set -euo pipefail
# === Configuration (replace "___" placeholders) ===
API_BASE_URL="https://api.1msg.io" # production 1MSG API base URL
CHANNEL_ID="___" # channel ID from 1MSG dashboard
API_TOKEN="___" # channel JWT token (Bearer)
# === Test data ===
TEST_PHONE="___" # client phone in international format
TEST_PRODUCTCONTEXT="___" # {{1}} product context
PHONE_NORM="$(printf '%s' "$TEST_PHONE" | tr -cd '0-9')"
URL="${API_BASE_URL%/}/${CHANNEL_ID}/sendList"
read -r -d '' PAYLOAD <<JSON || true
{
"phone": "${PHONE_NORM}",
"body": "Browse our catalog for spring promotion:",
"buttonText": "View catalog",
"action": "catalog",
"sections": [{"title":"Featured products","rows":[{"id":"prod_alpha","title":"Alpha Widget","description":"Compact model"},{"id":"prod_beta","title":"Beta Widget","description":"Standard model"}]},{"title":"Bundles","rows":[{"id":"bundle_starter","title":"Starter pack","description":"Two items included"},{"id":"bundle_pro","title":"Pro pack","description":"Four items included"}]}]
}
JSON
RESPONSE="$(curl -s -w '\n%{http_code}' -X POST "$URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${API_TOKEN}" \
-d "$PAYLOAD")"
HTTP_CODE="$(printf '%s' "$RESPONSE" | tail -n1)"
BODY="$(printf '%s' "$RESPONSE" | sed '$d')"
case "$BODY" in
*'"sent":true'*) ok=1 ;;
*) ok=0 ;;
esac
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ] && [ "$ok" -eq 1 ]; then
echo "Message sent to client."
else
echo "$BODY" >&2
exit 1
fi
Respuesta y estado de entrega
HTTP 2xx y JSON "sent": true significan que 1MSG aceptó el mensaje para envío — no que ya llegó al teléfono del cliente. Guarda el campo id (tipo wamid.…) para correlacionar los callbacks de entrega.
{
"sent": true,
"id": "wamid.HBgLMzgwNjM5...",
"message": "Message accepted for delivery"
}sentAceptado para envío — aún no en el teléfono del cliente
idGuárdalo; los callbacks de entrega y
hookInfose basan en él
La entrega llega después, como un callback aparte. Registra un webhook (POST …/webhook) y 1MSG enviará las actualizaciones de estado a tu endpoint HTTPS en un payload hooks[] de nivel superior.
{
"hooks": [
{
"id": "gBGGeSaGViBfAgnlzOSHEwK9O6F",
"type": "message",
"status": "sent",
"timestamp": "1654864094",
"recipient_id": "556123122026"
}
]
}statussent,delivered,read— o un estado de fallo cuando apliqueidCorrelaciona el callback con el
iddevuelto por el envíotimestampSegundos Unix, como cadena
Si prefieres no recibir callbacks, consulta GET {base}/{channel}/hookInfo?messageId=<id> en su lugar. En la práctica la entrega suele completarse en segundos — pero el contrato de la API no lo garantiza, así que nunca bloquees un flujo esperándola.
Errores frecuentes
| Estado | Respuesta | Causa |
|---|---|---|
| 200 | Message was not sent: empty body | Falta body en la petición. |
| 200 | Message was not sent: provide chatId, phone, bsuid, or username | Ningún destinatario que el canal pudiera resolver. |
| 200 | wrong file | El archivo no se pudo descargar ni subir — URL inalcanzable o base64 inválido. |
| 403 | access denied | El token es incorrecto, o pertenece a otro canal que el de la URL. |
| 200 | Message was not sent: filename | sendFile sin filename: el archivo no tiene extensión que enviar. |

