
How to Use whatsmeow-node with n8n
n8n is a self-hosted workflow automation platform — like Zapier, but open source. Its built-in WhatsApp node requires the official Cloud API (Meta Business verification, per-message pricing). With whatsmeow-node, you can connect n8n to WhatsApp for free using a simple REST bridge.
How It Works
n8n Workflow
↕ HTTP requests / webhooks
whatsmeow-node REST API (Express)
↕
WhatsApp
- Receiving: whatsmeow-node gets a WhatsApp message → POSTs it to an n8n webhook
- Sending: n8n workflow makes an HTTP request → whatsmeow-node REST API sends the message
Prerequisites
- A paired whatsmeow-node session (How to Pair)
- n8n running (self-hosted or cloud) —
npx n8nfor a quick start - Express:
npm install express
Step 1: Create the whatsmeow-node REST API
This server does two things: sends messages via REST and forwards incoming messages to n8n.
import { createClient } from "@whatsmeow-node/whatsmeow-node";
import express from "express";
const client = createClient({ store: "session.db" });
const app = express();
app.use(express.json());
// n8n webhook URL — you'll set this up in Step 3
const N8N_WEBHOOK_URL = process.env.N8N_WEBHOOK_URL ?? "http://localhost:5678/webhook/whatsapp";
// --- Sending endpoint (n8n calls this) ---
app.post("/api/send", async (req, res) => {
const { phone, message, groupJid } = req.body;
const jid = groupJid ?? `${phone}@s.whatsapp.net`;
try {
const resp = await client.sendMessage(jid, { conversation: message });
res.json({ sent: true, id: resp.id });
} catch (err) {
res.status(500).json({ error: "Failed to send" });
}
});
// --- Forward incoming messages to n8n ---
client.on("message", async ({ info, message }) => {
if (info.isFromMe) return;
const text =
(message.conversation as string) ??
(message.extendedTextMessage as { text?: string } | undefined)?.text;
// POST to n8n webhook
try {
await fetch(N8N_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
from: info.sender,
chat: info.chat,
pushName: info.pushName,
messageId: info.id,
text: text ?? null,
isGroup: info.isGroup,
timestamp: info.timestamp,
hasMedia: !!(
message.imageMessage ??
message.videoMessage ??
message.audioMessage ??
message.documentMessage
),
}),
});
} catch (err) {
console.error("Failed to forward to n8n:", err);
}
});
async function main() {
const { jid } = await client.init();
if (!jid) {
console.error("Not paired!");
process.exit(1);
}
await client.connect();
app.listen(3000, () => console.log("WhatsApp API on :3000"));
}
main().catch(console.error);
Step 2: Set Up the n8n Webhook Trigger
In n8n, create a new workflow:
- Add a Webhook node as the trigger
- Set method to
POST - Set path to
whatsapp(makes the URLhttp://localhost:5678/webhook/whatsapp) - Set the webhook URL as
N8N_WEBHOOK_URLin your REST API environment
Now every incoming WhatsApp message triggers this n8n workflow.
Step 3: Send Messages from n8n
Add an HTTP Request node to your n8n workflow:
- Method: POST
- URL:
http://localhost:3000/api/send - Body (JSON):
{
"phone": "{{ $json.from.replace('@s.whatsapp.net', '') }}",
"message": "Thanks for your message! We'll get back to you soon."
}
Example: Auto-Reply Workflow
A complete n8n workflow that auto-replies to messages:
[Webhook Trigger] → [IF: text contains "price"] → [HTTP Request: send reply]
→ [IF: text contains "help"] → [HTTP Request: send help menu]
→ [Default] → [HTTP Request: send acknowledgment]
Example: CRM Integration
Forward WhatsApp messages to a CRM and reply with the ticket number:
[Webhook Trigger] → [HTTP Request: create CRM ticket]
→ [Set: extract ticket ID]
→ [HTTP Request: send "Your ticket #{{ ticketId }} has been created"]
Example: AI-Powered Auto-Reply
Combine with OpenAI in n8n:
[Webhook Trigger] → [OpenAI Chat: generate reply]
→ [HTTP Request: send AI reply via whatsmeow-node]
Adding More Endpoints
Extend the REST API for n8n workflows that need more features:
// Send media
app.post("/api/send-media", async (req, res) => {
const { phone, filePath, mediaType, caption } = req.body;
const jid = `${phone}@s.whatsapp.net`;
const media = await client.uploadMedia(filePath, mediaType);
await client.sendRawMessage(jid, {
[`${mediaType}Message`]: {
URL: media.URL,
directPath: media.directPath,
mediaKey: media.mediaKey,
fileEncSHA256: media.fileEncSHA256,
fileSHA256: media.fileSHA256,
fileLength: String(media.fileLength),
mimetype: `${mediaType}/*`,
...(caption && { caption }),
},
});
res.json({ sent: true });
});
// Send to group
app.post("/api/send-group", async (req, res) => {
const { groupJid, message } = req.body;
const resp = await client.sendMessage(groupJid, { conversation: message });
res.json({ sent: true, id: resp.id });
});
// Mark as read
app.post("/api/read", async (req, res) => {
const { messageId, chat, sender } = req.body;
await client.markRead([messageId], chat, sender);
res.json({ ok: true });
});
Common Pitfalls
The whatsmeow-node REST API and n8n must both be running. Use PM2 or Docker Compose to manage them together.
n8n's webhook URL must be accessible from the whatsmeow-node server. In Docker, use the container name or network alias, not localhost.
n8n workflows can execute very fast. If your workflow sends multiple messages, add a Wait node (1-3 seconds) between sends to avoid WhatsApp rate limiting.







