Enviar mensaje de WhatsApp

POST
/api/meta/whatsapp/send

Envía texto o un adjunto por URL (nunca ambos) a un contacto desde un número de WhatsApp conectado. El token del negocio se resuelve en el servidor.

El body es el mismo que el de Messenger a propósito (ADR 0019): un cliente que atiende los tres canales solo cambia la URL. pageId es el phone_number_id del número conectado y recipientId es el teléfono del contacto en formato internacional.

Destino en una de dos formas:

  • conversationId solo, tomado del conversation.id del webhook.
  • pageId + recipientId; si además viene conversationId, debe coincidir con ese par. El teléfono se normaliza a dígitos (sin +, espacios ni guiones), igual que el wa_id de los entrantes.

Ventana de 24 horas. Resender la evalúa localmente antes de llamar a Meta: si el contacto no escribió en las últimas 24 horas (o nunca escribió), responde 409 con requiresTemplate: true sin consumir cuota. En ese caso hay que usar POST /api/meta/whatsapp/templates/send.

Idempotency-Key es obligatoria en WhatsApp: un duplicado le llega al teléfono del cliente final.

Texto: máximo 4096 caracteres. Adjuntos: image, video y audio llegan con ese mismo tipo y se ven en el chat; file se envía como document de Cloud API.

Pie de foto. attachment.caption (opcional, solo WhatsApp) es el texto que se ve debajo del adjunto, en el mismo mensaje. Vale para image, video y file; en audio responde 400 (attachment_caption_unsupported) porque WhatsApp no lo muestra. Máximo 1024 caracteres. Vacío o null es "sin pie de foto".

Resender no hospeda media: Meta descarga el archivo desde la URL y lo cachea ~10 minutos, así que la URL no puede ser de un solo uso ni caducar en segundos. Si el origen no responde, el envío falla con code: attachment_fetch_failed.

AuthorizationBearer <token>

API key opaca del tenant, creada en Settings → API Tokens. Una key revocada, inexistente o malformada responde 401 {"error":"unauthorized"}.

In: header

Header Parameters

Idempotency-Key*string

Obligatoria en WhatsApp. Clave elegida por el cliente (1–200 caracteres). Repetirla devuelve 200 con el resultado original y resender.idempotentReplay: true, sin volver a llamar a Meta ni consumir cuota. Ausente, vacía o de más de 200 caracteres responde 400.

Length1 <= length <= 200
x-request-id?string

Identificador de correlación opcional que se copia a los logs del servidor.

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Destino del envío. O conversationId solo, o pageId + recipientId (opcionalmente con conversationId, que debe coincidir con el par).

Exactamente uno de reply o attachment.

Exactamente uno de reply o attachment.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/api/meta/whatsapp/send" \  -H "Idempotency-Key: order-8841-shipped" \  -H "Content-Type: application/json" \  -d '{    "conversationId": "6f0e5a2c-8a5e-4a3d-9c2b-1f2e3d4c5b6a",    "reply": "¡Hola! Tu pedido #8841 ya salió."  }'
{  "meta": {    "messaging_product": "whatsapp",    "contacts": [      {        "input": "5215512345678",        "wa_id": "5215512345678"      }    ],    "messages": [      {        "id": "wamid.HBgNNTIxNTUxMjM0NTY3OBUCABEYEjBBQkNERUYxMjM0NTY3ODkwAA=="      }    ]  },  "resender": {    "conversationId": "6f0e5a2c-8a5e-4a3d-9c2b-1f2e3d4c5b6a",    "messageId": "0c9d2f6e-3b7a-4e1d-8f5c-2a1b3c4d5e6f",    "status": "sent"  }}