Conecta WaMundo con las herramientas que ya usas: recibe tus mensajes y eventos en Make, n8n, Zapier o Google Sheets y envía WhatsApp desde ellas con la API.
Crea escenarios visuales que reciben tus mensajes de WaMundo y responden por WhatsApp.
Flujos de automatización de código abierto, en la nube o en tu propio servidor.
Guarda tus mensajes en una hoja de cálculo y envía WhatsApp desde sus filas.
Avisos de pedidos de tu tienda WooCommerce por WhatsApp.
Avisos de cobros y pagos con Mercado Pago.
Mensajes de pedidos de tu tienda Shopify.
Recordatorios de citas desde Google Calendar.
POST /send/whatsapp) o un envío masivo (POST /send/whatsapp.bulk).wa_send; para envíos masivos, «Enviar WA Masivo», wa_send_bulk) y copia su secret.GET https://wamundo.com/api/get/wa.accounts?secret=TU_API_SECRET (campo unique; la clave necesita el permiso «Ver Cuentas WA», get_wa_accounts). Cambia cada vez que vuelves a vincular el número.data.phone, data.message, data.reply_to...).https://wamundo.com/api/send/whatsapp · Method: POST · Body type: Multipart/form-data (también sirve application/x-www-form-urlencoded).secret, account, recipient (arrastra data.reply_to del webhook), type = text y message.status y data.messageId) en los módulos siguientes.status de la respuesta es igual a 200. Así un secret o una cuenta incorrectos no pasan como si todo hubiera ido bien.Status Code 200 aunque haya un error (la API siempre responde HTTP 200), así que no basta con eso: la respuesta JSON debe traer "status": 200. Si trae otro valor, mira message.webhook.test que no trae reply_to. Escribe un mensaje real al número conectado para tener todos los campos.status del JSON (200 = enviado; 401, 404... = error) y el motivo en message. Comprueba ese campo con un filtro o una condición.status 401 en el JSON): el secret es incorrecto, tiene espacios sobrantes o la clave fue revocada. Copia el secret de nuevo desde Desarrolladores → Claves API.status 404 en el JSON): en account pusiste el número de teléfono, o el ID único ya cambió porque volviste a vincular el número. Consulta /get/wa.accounts y usa el valor actual de unique.secret del aviso con la clave de tu webhook.message.status). Cada aviso se envía una sola vez, sin reintentos, y tras 200 fallos seguidos o 3 días sin ningún 2xx el webhook se pausa y se vuelve a probar una vez por hora.POST /send/whatsapp) o un envío masivo (POST /send/whatsapp.bulk).wa_send; para envíos masivos, «Enviar WA Masivo», wa_send_bulk) y copia su secret.GET https://wamundo.com/api/get/wa.accounts?secret=TU_API_SECRET (campo unique; la clave necesita el permiso «Ver Cuentas WA», get_wa_accounts). Cambia cada vez que vuelves a vincular el número.status 200. Reemplaza TU_API_SECRET y TU_ACCOUNT_UNIQUE_ID. Descargaswamundo.$json.body: por ejemplo {{ $json.body.data.message }}. Las cabeceras están en $json.headers.https://wamundo.com/api/send/whatsapp.secret, account, recipient ({{ $json.body.data.reply_to }}), type = text y message.{{ $json.status }} es igual a 200 (número). La API responde siempre con código HTTP 200, así que el nodo HTTP no marca el error por sí solo; por la salida «false» sale la respuesta con el motivo en message.Para comprobar X-WaMundo-Signature necesitas los bytes exactos del cuerpo: en el nodo Webhook activa Options → Raw Body y añade un nodo Code. Es un punto de partida: la forma en que n8n entrega el cuerpo crudo depende de la versión, así que revísalo en la salida del nodo y pruébalo en tu instancia.
// Nodo Code (Run Once for All Items), después del Webhook con Options > Raw Body activado
const crypto = require('crypto');
const SECRETO = 'TU_SECRET_DEL_WEBHOOK'; // clave del webhook en WaMundo, no el secret de la API
const item = $input.first();
const ts = item.json.headers['x-wamundo-timestamp'];
const firma = item.json.headers['x-wamundo-signature'] || '';
const raw = await this.helpers.getBinaryDataBuffer(0, 'data'); // bytes exactos del cuerpo
const esperada = 'sha256=' + crypto.createHmac('sha256', SECRETO).update(ts + '.').update(raw).digest('hex');
const a = Buffer.from(esperada);
const b = Buffer.from(firma);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
throw new Error('Firma inválida');
}
return $input.all();
Importar crypto en el nodo Code exige, en n8n autoalojado, definir la variable NODE_FUNCTION_ALLOW_BUILTIN=crypto. En n8n Cloud el nodo Code puede restringir los módulos integrados, así que allí no podemos asegurar que funcione.
"status": 200. La plantilla ya lo comprueba con un nodo IF.webhook.test: llega al nodo Webhook, pero la plantilla lo descarta a propósito (solo responde a type = whatsapp).$json.body, no en $json.webhook.test que no trae reply_to. Escribe un mensaje real al número conectado para tener todos los campos.status del JSON (200 = enviado; 401, 404... = error) y el motivo en message. Comprueba ese campo con un filtro o una condición.status 401 en el JSON): el secret es incorrecto, tiene espacios sobrantes o la clave fue revocada. Copia el secret de nuevo desde Desarrolladores → Claves API.status 404 en el JSON): en account pusiste el número de teléfono, o el ID único ya cambió porque volviste a vincular el número. Consulta /get/wa.accounts y usa el valor actual de unique.type y evita responder a mensajes automáticos. Si el número también tiene la IA de WaMundo activa, contestarán las dos: apaga una de ellas.message.status). Cada aviso se envía una sola vez, sin reintentos, y tras 200 fallos seguidos o 3 días sin ningún 2xx el webhook se pausa y se vuelve a probar una vez por hora.POST /send/whatsapp) o un envío masivo (POST /send/whatsapp.bulk).wa_send; para envíos masivos, «Enviar WA Masivo», wa_send_bulk) y copia su secret.GET https://wamundo.com/api/get/wa.accounts?secret=TU_API_SECRET (campo unique; la clave necesita el permiso «Ver Cuentas WA», get_wa_accounts). Cambia cada vez que vuelves a vincular el número.data__message o data__reply_to.https://wamundo.com/api/send/whatsapp · Payload Type: Form.secret, account, recipient (el campo data__reply_to del disparador), type = text y message.status de la respuesta de WaMundo es igual a 200. Zapier puede mostrar además un campo status propio del paso: comprueba en Test step cuál trae el 200 de WaMundo, o exige que exista data__messageId."status": 200 (la API siempre responde HTTP 200, incluso con errores) y llegar un WhatsApp al destinatario.webhook.test que no trae reply_to. Escribe un mensaje real al número conectado para tener todos los campos.status del JSON (200 = enviado; 401, 404... = error) y el motivo en message. Comprueba ese campo con un filtro o una condición.status 401 en el JSON): el secret es incorrecto, tiene espacios sobrantes o la clave fue revocada. Copia el secret de nuevo desde Desarrolladores → Claves API.status 404 en el JSON): en account pusiste el número de teléfono, o el ID único ya cambió porque volviste a vincular el número. Consulta /get/wa.accounts y usa el valor actual de unique.X-WaMundo-Signature por sí solo. Como alternativa, añade un paso Filter que continúe solo si el campo secret del aviso coincide con la clave de tu webhook.message.status). Cada aviso se envía una sola vez, sin reintentos, y tras 200 fallos seguidos o 3 días sin ningún 2xx el webhook se pausa y se vuelve a probar una vez por hora.POST /send/whatsapp) o un envío masivo (POST /send/whatsapp.bulk).wa_send; para envíos masivos, «Enviar WA Masivo», wa_send_bulk) y copia su secret.GET https://wamundo.com/api/get/wa.accounts?secret=TU_API_SECRET (campo unique; la clave necesita el permiso «Ver Cuentas WA», get_wa_accounts). Cambia cada vez que vuelves a vincular el número.Un script dentro de tu hoja recibe el webhook de WaMundo (doPost) y añade una fila por mensaje. Además trae una función para enviar WhatsApp desde la hoja.
TU_...: SECRET es el secreto del webhook de WaMundo (Desarrolladores → Webhooks, botón «Copiar secreto de webhook al portapapeles» en la fila del webhook) y no es el secret de la API./exec./exec.secret del cuerpo JSON contra la constante SECRET.La función enviarWhatsApp(telefono, texto) usa UrlFetchApp para llamar a POST /send/whatsapp. Rellena API_SECRET y ACCOUNT_ID, elige la función en el editor de Apps Script, pulsa Ejecutar (la primera vez Google pide autorización) y mira el resultado en el Registro de ejecución. Puedes llamarla desde tus propias funciones, por ejemplo recorriendo las filas de una pestaña. Google limita las llamadas diarias de UrlFetchApp: consulta sus cuotas.
Sigue la guía de la herramienta que prefieras hasta recibir el webhook y añade un módulo de Google Sheets a continuación. Conecta tu cuenta de Google cuando la herramienta te lo pida.
Asigna a cada columna un campo del aviso: data.phone, data.message, created_at y delivery_id. Para el camino inverso (enviar desde una fila), usa el disparador «Watch rows» / «New row» de la hoja seguido del paso de envío de la guía de esa herramienta.
enviarWhatsApp("+584121234567", "Hola") con un número tuyo y mira el registro de ejecución: debe mostrar "status":200 en el JSON (el código HTTP es siempre 200, también con errores)./exec use el código actualizado.message.status, que no trae el mismo contenido).doPost a mano: es normal; doPost solo funciona cuando lo llama el webhook, porque no hay una petición real al ejecutarlo desde el editor.status del JSON (200 = enviado; 401, 404... = error) y el motivo en message. Comprueba ese campo con un filtro o una condición.status 401 en el JSON): el secret es incorrecto, tiene espacios sobrantes o la clave fue revocada. Copia el secret de nuevo desde Desarrolladores → Claves API.status 404 en el JSON): en account pusiste el número de teléfono, o el ID único ya cambió porque volviste a vincular el número. Consulta /get/wa.accounts y usa el valor actual de unique.message.status). Cada aviso se envía una sola vez, sin reintentos, y tras 200 fallos seguidos o 3 días sin ningún 2xx el webhook se pausa y se vuelve a probar una vez por hora.Al crear el webhook en Desarrolladores → Webhooks eliges los eventos y el formato. Con el formato JSON, WaMundo hace un POST como este (los datos exactos dependen del evento; mira la documentación de Webhooks):
{
"secret": "tu_clave_del_webhook",
"type": "whatsapp",
"data": {
"id": 789,
"phone": "+584141234567",
"message": "Hola, quiero información",
"attachment": false,
"timestamp": 1679900000,
"reply_to": "+584141234567"
},
"event": "whatsapp",
"delivery_id": "0b6f6c0e-6d3c-4a8e-9a53-7c1f1d1e2b44",
"created_at": "2026-10-27T15:04:05Z"
}
Cada envío incluye las cabeceras X-WaMundo-Signature (sha256= y el HMAC-SHA256 de timestamp + "." + cuerpo exacto), X-WaMundo-Timestamp, X-WaMundo-Event y X-WaMundo-Delivery. Para responder al remitente usa data.reply_to como recipient.
Otros eventos que puedes marcar: message.status (enviado, entregado, leído o fallido), conversation.created y conversation.closed, además de sms y ussd. El botón «Enviar prueba» manda un evento webhook.test.
Si usas el Embudo de ventas puedes marcar también deal.created, deal.moved, deal.won, deal.lost, deal.reopened y deal.deleted, por ejemplo para avisar a tu equipo o registrar una venta cuando un trato pasa a Ganado. Un trato creado directamente en Ganado o Perdido envía deal.created y enseguida deal.won o deal.lost; al eliminar una etapa con tratos se envía un evento por cada trato reasignado (máximo 50). Requiere un plan con webhooks.
Haz un POST a https://wamundo.com/api/send/whatsapp como multipart/form-data o application/x-www-form-urlencoded con estos campos:
| Campo | Qué poner |
|---|---|
secret | El secret de tu clave de API (Desarrolladores → Claves API). La clave necesita el permiso «Enviar WhatsApp» (wa_send). |
account | El ID único de la cuenta de WhatsApp (unique de /get/wa.accounts), no el número. |
recipient | Número en formato E.164 (+584121234567), número local, grupo (1234567890@g.us) o el data.reply_to del mensaje recibido. |
type | text (valor por defecto) para texto. También admite media, document, poll, location, contact_card y sticker. |
message | El texto del mensaje. |
{
"status": 200,
"message": "WhatsApp chat has been queued for sending!",
"data": { "messageId": 456 }
}
Responde siempre con código HTTP 200, también cuando hay un error: el resultado real está en el campo status del JSON (200 = enviado; 401, 404... = error, con el motivo en message). Si todo va bien trae status 200 y el messageId. Para enviar a muchos destinatarios existe POST /send/whatsapp.bulk (necesita el permiso wa_send_bulk). Todos los endpoints están en la documentación de la API.
Dos archivos listos para usar. Reemplaza los valores TU_... por los tuyos antes de activarlos y no los compartas con tus claves puestas.
Workflow importable (Import from File): Webhook → filtro → respuesta automática por WhatsApp.
{
"name": "WaMundo - Responder mensajes de WhatsApp",
"nodes": [
{
"parameters": {
"content": "## WaMundo: responder mensajes de WhatsApp\n1. Reemplaza TU_API_SECRET (Desarrolladores > Claves API) y TU_ACCOUNT_UNIQUE_ID (GET /get/wa.accounts) en el nodo HTTP, o mejor guárdalos en una credencial.\n2. En WaMundo crea un webhook con el evento WhatsApp, formato JSON, y la Production URL del nodo Webhook.\n3. Activa el workflow.\n4. El nodo IF final comprueba que la respuesta de WaMundo traiga status 200 (la API siempre responde HTTP 200, incluso con errores). Si no, mira message en su salida false.",
"height": 260,
"width": 380
},
"id": "6c1a0b52-3f0e-4d63-9a41-0b7a1f5d2e01",
"name": "Instrucciones",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-200,
-120
]
},
{
"parameters": {
"httpMethod": "POST",
"path": "wamundo",
"options": {}
},
"id": "b3d9f7a4-5c21-4e8b-8f0a-2a6d4c9e7b02",
"name": "Webhook WaMundo",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
200,
160
],
"webhookId": "e5a8c3d1-7b94-4f26-a0c8-91d2b6f4a303"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "1f0e2d3c-4b5a-4968-8778-6a5b4c3d2e11",
"leftValue": "={{ $json.body.type }}",
"rightValue": "whatsapp",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c22",
"leftValue": "={{ $json.body.data.message }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
},
{
"id": "3b2c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d33",
"leftValue": "={{ $json.body.data.reply_to }}",
"rightValue": "@g.us",
"operator": {
"type": "string",
"operation": "notContains"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "d7e4a1b8-2c93-4d05-b6f1-8a3e5c7d9f04",
"name": "¿Es un mensaje de WhatsApp?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
440,
160
]
},
{
"parameters": {
"method": "POST",
"url": "https://wamundo.com/api/send/whatsapp",
"sendBody": true,
"contentType": "form-urlencoded",
"specifyBody": "keypair",
"bodyParameters": {
"parameters": [
{
"name": "secret",
"value": "TU_API_SECRET"
},
{
"name": "account",
"value": "TU_ACCOUNT_UNIQUE_ID"
},
{
"name": "recipient",
"value": "={{ $json.body.data.reply_to }}"
},
{
"name": "type",
"value": "text"
},
{
"name": "message",
"value": "Recibimos tu mensaje. En breve te respondemos."
}
]
},
"options": {}
},
"id": "a9c2e6f3-8d14-4b57-9e0a-3f7b1d5c8e05",
"name": "Responder por WaMundo",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
700,
60
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "4c3d5e6f-7a8b-4c9d-8e0f-2a3b4c5d6e44",
"leftValue": "={{ $json.status }}",
"rightValue": 200,
"operator": {
"type": "number",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "e8f5b2c9-3d04-4e16-a7b2-9c4f6d8e0a06",
"name": "¿WaMundo aceptó el envío?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
960,
60
]
},
{
"parameters": {},
"id": "f9a6c3d0-4e15-4f27-b8c3-0d5a7e9f1b07",
"name": "Revisa status y message",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
1220,
160
]
}
],
"connections": {
"Webhook WaMundo": {
"main": [
[
{
"node": "¿Es un mensaje de WhatsApp?",
"type": "main",
"index": 0
}
]
]
},
"¿Es un mensaje de WhatsApp?": {
"main": [
[
{
"node": "Responder por WaMundo",
"type": "main",
"index": 0
}
],
[]
]
},
"Responder por WaMundo": {
"main": [
[
{
"node": "¿WaMundo aceptó el envío?",
"type": "main",
"index": 0
}
]
]
},
"¿WaMundo aceptó el envío?": {
"main": [
[],
[
{
"node": "Revisa status y message",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": []
}
Apps Script: guarda cada mensaje recibido en una hoja y envía WhatsApp con enviarWhatsApp().
/**
* WaMundo + Google Sheets
*
* 1) Recibe los avisos (webhooks) de WaMundo y guarda cada mensaje como una fila.
* 2) Envía un WhatsApp desde la hoja con enviarWhatsApp().
*
* Instalación: en tu hoja, Extensiones > Apps Script, pega este código y reemplaza los valores TU_...
* Luego Implementar > Nueva implementación > Aplicación web
* (Ejecutar como: Yo · Quién tiene acceso: Cualquier persona)
* y pega la URL que termina en /exec en WaMundo (Desarrolladores > Webhooks) con formato JSON.
*/
// Secreto del webhook (en WaMundo: Desarrolladores > Webhooks, botón "Copiar secreto de webhook al portapapeles"). NO es el secret de la API.
var SECRET = "TU_SECRET_DEL_WEBHOOK";
var HOJA = "Mensajes";
// Solo para enviar mensajes (WaMundo: Desarrolladores > Claves API). Guárdalos aquí, no en las celdas.
var API_SECRET = "TU_API_SECRET";
var ACCOUNT_ID = "TU_ACCOUNT_UNIQUE_ID";
// Recibe el aviso de WaMundo y añade una fila: fecha, tipo, teléfono, mensaje, id del envío.
function doPost(e) {
var cuerpo;
try {
cuerpo = JSON.parse(e.postData.contents);
} catch (err) {
return ContentService.createTextOutput("formato no JSON");
}
// Apps Script no permite leer cabeceras: no se puede verificar la firma HMAC,
// así que se comprueba el campo "secret" que viaja dentro del cuerpo.
if (cuerpo.secret !== SECRET) {
return ContentService.createTextOutput("forbidden");
}
var data = cuerpo.data || {};
var libro = SpreadsheetApp.getActiveSpreadsheet();
var hoja = libro.getSheetByName(HOJA) || libro.insertSheet(HOJA);
hoja.appendRow([new Date(), cuerpo.type, data.phone, data.message, cuerpo.delivery_id]);
return ContentService.createTextOutput("ok");
}
// Envía un WhatsApp. Ejemplo: enviarWhatsApp("+584121234567", "Hola desde mi hoja")
function enviarWhatsApp(telefono, texto) {
var respuesta = UrlFetchApp.fetch("https://wamundo.com/api/send/whatsapp", {
method: "post",
payload: {
secret: API_SECRET,
account: ACCOUNT_ID,
recipient: telefono,
type: "text",
message: texto
},
muteHttpExceptions: true
});
Logger.log(respuesta.getContentText());
return respuesta.getContentText();
}