POST /messages
Enviar un mensaje de WhatsApp. Soporta texto, imagen, video, audio, documento, template e interactivos. Scope requerido:messages:write
Body
Enviar texto
Enviar template
Enviar imagen
Response 201
Idempotencia
Incluí el headerIdempotency-Key para evitar envíos duplicados. Ver Idempotencia.
Errores frecuentes
Comportamiento
- Si el contacto no existe, se crea automáticamente con el número.
- Si no hay conversación previa, se crea una nueva.
- El mensaje se encola y se envía de forma asíncrona. El status inicial es
queued. - Consultá el estado final con
GET /messages/:id.
POST /messages/batch
Encolar hasta 100 mensajes en una sola llamada. Pensado para envíos masivos (avisos, solicitudes de reseña, recordatorios). Cada item se acepta, saltea o rechaza de forma independiente — un destinatario inválido nunca tumba el lote. Scope requerido:messages:write (el mismo que POST /messages — cualquier API key que ya envía mensajes sirve sin cambios).
Responde 202 Accepted apenas persiste y encola; la entrega ocurre de forma asíncrona al ritmo de envío de tu cuenta. Los envíos masivos usan prioridad de campaña, así que nunca le quitan turno al chat en vivo.
Body
El campo raíz para el canal compartido se llama
defaultPhoneNumberId (no phoneNumberId). Por item, el campo sí es phoneNumberId y, si lo omitís, hereda el defaultPhoneNumberId.Ejemplo — solicitudes de reseña
Response 202
Trae un detalle por item, en el mismo orden en que los mandaste:
items tiene uno de tres resultados:
Reconciliación recomendada
MandáclientRef con tu propio identificador (número de pedido, ID de solicitud) en cada item. Después suscribite al webhook message.status_changed: cada cambio de estado (sent → delivered → read o failed) trae tu clientRef de vuelta en data.clientRef, así matcheás los eventos contra tus registros sin guardar ningún mapping.
Para volúmenes mayores a 100, mandá varias requests (por ejemplo en tandas de 100). El rate limit de la API v2 aplica por request, no por mensaje — ver Rate limits.
Errores frecuentes
Los errores por item (teléfono o canal inválido, campo faltante) no devuelven un código de error global: vienen dentro de
items[].error con result: "failed".
GET /messages/:id
Consultar un mensaje por ID. Útil para verificar el estado de envío. Scope requerido:messages:read
Response 200
POST /messages/:waMessageId/read
Marcar un mensaje entrante como leído por suwaMessageId (el mismo que llega en el webhook message.received). Conveniente cuando tenés a mano el waMessageId en lugar del conversationId.
Scope requerido: conversations:write
POST /conversations/:id/read — manda el read receipt a Meta y resetea el unreadCount de la conversación padre.
waMessageId no existe o no es inbound.