Enviar mensajes de WhatsApp con Node.js
Código mínimo funcional contra la Cloud API, sin dependencias externas.
Necesitas Node.js 18 o superior (para fetch nativo), tu Phone Number ID y un token de acceso.
1. Enviar un mensaje de texto libre
Solo funciona si el usuario te escribió en las últimas 24 horas (ventana de servicio abierta):
const PHONE_NUMBER_ID = process.env.WHATSAPP_PHONE_NUMBER_ID;
const ACCESS_TOKEN = process.env.WHATSAPP_ACCESS_TOKEN;
async function enviarTexto(numeroDestino, mensaje) {
const respuesta = await fetch(
`https://graph.facebook.com/v26.0/${PHONE_NUMBER_ID}/messages`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messaging_product: 'whatsapp',
to: numeroDestino, // formato E.164 sin "+", ej. "34600000000"
type: 'text',
text: { body: mensaje }
})
}
);
const data = await respuesta.json();
if (!respuesta.ok) throw new Error(JSON.stringify(data));
return data;
}
enviarTexto('34600000000', 'Hola, ¿en qué puedo ayudarte?')
.then(console.log)
.catch(console.error);
2. Enviar una plantilla (fuera de la ventana de 24h)
Si no hay conversación abierta, tienes que usar una plantilla aprobada por Meta, referenciada por su nombre e idioma:
async function enviarPlantilla(numeroDestino, nombrePlantilla, idioma, parametros = []) {
const respuesta = await fetch(
`https://graph.facebook.com/v26.0/${PHONE_NUMBER_ID}/messages`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messaging_product: 'whatsapp',
to: numeroDestino,
type: 'template',
template: {
name: nombrePlantilla,
language: { code: idioma }, // ej. "es" o "es_ES"
components: parametros.length ? [{
type: 'body',
parameters: parametros.map(texto => ({ type: 'text', text: texto }))
}] : []
}
})
}
);
const data = await respuesta.json();
if (!respuesta.ok) throw new Error(JSON.stringify(data));
return data;
}
// Ejemplo: plantilla "confirmacion_pedido" con 1 variable {{1}}
enviarPlantilla('34600000000', 'confirmacion_pedido', 'es', ['#48213']);
Comprueba antes qué categoría tendrá tu plantilla (afecta al coste) con el comprobador de categoría.
3. Recibir mensajes y estados (webhooks)
El envío es solo la mitad: para recibir mensajes entrantes y confirmaciones de entrega necesitas un endpoint propio que reciba los webhooks de Meta. Cómo montarlo y qué significa cada campo, en la guía de webhooks, y valida cualquier payload real con el validador de webhooks.