Cómo reaccionar a un mensaje por la API de WhatsApp
POST /messages con type reaction, el waMessageId y un emoji. Un emoji vacío saca la reacción. Si la ventana de 24 horas está cerrada, la API responde 403.
Respuesta corta: para poner un emoji sobre un mensaje, POST https://api.waspytech.com/api/v2/messages con type: "reaction", el waMessageId en reaction.message_id y el emoji en reaction.emoji. Un emoji "" saca la reacción que habías puesto. Si la ventana de 24 horas está cerrada, la API responde 403 SERVICE_WINDOW_EXPIRED.
El contrato está en Messages. El scope es messages:write.
El pedido
message_id es el waMessageId del webhook message.received (empieza con wamid.), no el UUID interno del mensaje en Waspy.
curl -sS -X POST "https://api.waspytech.com/api/v2/messages" \
-H "Authorization: Bearer $WASPY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reaccion-wamid-HBgL-ok" \
-d '{
"phoneNumberId": "UUID-DEL-CANAL",
"to": "+5491126032641",
"type": "reaction",
"reaction": { "message_id": "wamid.HBgL...", "emoji": "👍" }
}'
phoneNumberId es el id de GET /api/v2/channels, el UUID del canal en Waspy. to va en E.164. La respuesta 201 trae el mensaje con status: "queued", origin: "external_api" y waMessageId: null hasta que sale. El estado siguiente llega en message.status_changed.
Para sacarla:
curl -sS -X POST "https://api.waspytech.com/api/v2/messages" \
-H "Authorization: Bearer $WASPY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reaccion-wamid-HBgL-sacar" \
-d '{
"phoneNumberId": "UUID-DEL-CANAL",
"to": "+5491126032641",
"type": "reaction",
"reaction": { "message_id": "wamid.HBgL...", "emoji": "" }
}'
La clave de sacar es otra: la primera ya quedó guardada 24 horas con el 👍. Idempotency-Key se compara como texto, hasta 256 caracteres. El mecanismo está en idempotencia y scopes.
La ventana de 24 horas
Una reacción no es una plantilla. El envío por API exige la ventana abierta, igual que un texto o una foto. Si serviceWindowExpiresAt está vacío o vencido, no se encola: 403 SERVICE_WINDOW_EXPIRED.
Esa fecha está en GET /api/v2/conversations/:id. La regla de negocio está en la ventana de 24 horas.
| Pedido | Ventana cerrada | Ventana abierta |
|---|---|---|
type: "reaction" | 403, no sale | Se encola |
type: "text" | 403 | Mensaje de servicio |
type: "template" | Sale, y Meta lo cobra según la categoría | También se cobra, desde el 1 de octubre de 2026 |
Para qué sirve, y para qué no
Sirve para acusar recibo de un “listo, pagué” o de un comprobante sin meter otro globo de “recibido, gracias, ya lo vemos”. El cliente ve el emoji sobre su mensaje.
No reemplaza:
- el aviso de despacho (eso es una plantilla de utilidad, con el código de seguimiento);
- una pregunta de talle (hace falta texto);
- el handoff. La reacción queda como
external_api. Una persona en el inbox queda comopanel. Si tu bot tiene que callarse cuando el equipo escribe, la señal es esa, en handoff con origin.
Tampoco hay un POST /templates para crear la plantilla que vas a necesitar cuando la ventana cierre. Las plantillas se leen con GET /templates, GET /templates/:id y GET /templates/by-name/:name, y se arman en el panel. El listado está en listar plantillas.
La API está en Scale y Enterprise. El detalle de planes está en Planes y en la página de la API. Para probar el número, el registro son 10 días sin tarjeta.
