Package Information
Available Nodes
Documentation generating…
Documentation
n8n-nodes-whatsapp-personal
By Diginno — hệ thống gọn gàng, vận hành yên tâm.
Diginno xây dựng dịch vụ tự động hoá vận hành (Lark, n8n, AI agent) cho SME.
An n8n community node to send and receive WhatsApp messages from a
personal number — no official Meta Business Cloud API, no business
verification, no template-message approval. Just a phone number, a QR scan,
and a self-hosted bridge.
Why this exists
n8n's built-in WhatsApp node only supports the official Meta Business
Cloud API: a verified business number, and template-message approval for
any outbound message you initiate. That's the wrong tool for ad-hoc
personal/internal use — a solo founder sending themselves a balance alert,
an SME notifying a small team, a side project that just needs to send a
plain-text WhatsApp message without going through Meta's business approval
process.
This package fills that gap: a thin n8n node backed by a small self-hosted
bridge (@whiskeysockets/baileys — the WhatsApp Web protocol).
Two parts
bridge/ Standalone Node.js service — connects to WhatsApp via Baileys,
exposes HTTP endpoints (/send, /health, /qr, /groups, ...).
Runs continuously, holds the linked WhatsApp session(s).
See bridge/README.md — includes multi-account support.
src/ The n8n community node itself (TypeScript) — thin HTTP client
calling into bridge/. This is what you install into n8n.
They are decoupled on purpose: the bridge has no n8n dependency and can be
tested standalone with curl; the node has no Baileys dependency and just
speaks HTTP. Internally the node is split into a pure client
(WhatsappClient.ts), a swappable transport (WhatsappTransports.ts — real
n8n HTTP calls, or an in-memory fake for tests), and a shared operation
executor (WhatsappExecutor.ts) — see comments in src/actions/common/ if
you're extending it.
Resources & operations
| Resource | Operation | Calls | Notes |
|---|---|---|---|
| Message | Send | POST /send |
chatId accepts a plain phone number (auto-normalized to <digits>@s.whatsapp.net) or a full JID/group JID |
| Message | Edit | POST /edit |
Edit the text of a message this account already sent (needs the messageId a prior Send returned) |
| Message | Send Media | POST /send-media |
Send an image/video/audio/document. Source is either Binary Data (from a previous node, e.g. HTTP Request with Response Format: File) or a URL the bridge downloads server-side. Audio is auto-converted to Ogg/Opus so it renders as a native voice note. |
| Message | Send Typing Indicator | POST /typing |
Cosmetic "typing…" bubble, no message sent |
| Message | Get Recent | GET /messages |
Drains the bridge's incoming-message queue — each call returns only what arrived since the last call, not full history. Pair with a Schedule Trigger + Data Table to build message history. |
| Connection | Get Status | GET /health |
Bridge reachability + WhatsApp connection state |
| Connection | Get QR Code | GET /qr |
Returns the current pairing QR as a PNG image — n8n renders it inline in the execution result panel, so you can scan it straight off screen. 404s once already connected. |
| Group | List | GET /groups |
Lists every group the linked account is in — use the returned id as Chat ID to send to a group |
| Contact | Check Number | POST /contacts/check |
WhatsApp has no "friends list" — this is the closest primitive: confirms whether a number is registered before you message it |
| Chat | Get Info | GET /chat/:id |
Name and (for groups) participant JIDs of a chat |
Credential (WhatsApp Personal Bridge API): baseUrl (defaulthttp://localhost:3000) + optional apiKey (only needed if the bridge was
started with WA_BRIDGE_API_KEY set).
Quick start
# 1. Build the node
npm install && npm run build
# 2. Start a bridge account and scan the QR
cd bridge && npm install
cp accounts/.env.example accounts/main.env # edit: set WA_PORT, generate WA_BRIDGE_API_KEY
./start.sh main # prints QR — scan with WhatsApp: Settings > Linked Devices
# 3. Install the node into your n8n instance (see "Production deploy" below),
# or run the bundled dev environment:
cd ../n8n_dev && docker-compose up -d # http://localhost:5683
Full dev/deploy walkthroughs: bridge/README.md (bridge
setup, multi-account) and the sections below (node install, example
workflows).
Multiple WhatsApp accounts
Each account = one bridge process (own port, own session, own credential in
n8n). Managed via bridge/accounts/ + start.sh / stop.sh / status.sh.
See bridge/README.md.
Example workflow: automated balance alert (e.g. SePay)
Webhook (bank/payment gateway POST)
→ Set/Code node: format message from the payload
→ WhatsApp Personal node (Message → Send): chatId = your number, message = formatted text
Example format expression in the Set node (SePay-style payload):
Biến động số dư
{{$json.transferType === 'in' ? '+' : '-'}}{{$json.transferAmount}}đ
ND: {{$json.content}}
Số dư: {{$json.accumulated}}đ
Example workflow: message history → Data Table
Schedule Trigger (every few minutes)
→ WhatsApp Personal (Message → Get Recent)
→ Data Table (Insert into a "messages" table)
→ Data Table (Upsert into a "contacts" table, matched on sender JID)
Because Get Recent drains the bridge's queue and returns zero items
when nothing new arrived, this composes directly into a Data Table Insert
with no Filter node needed in between.
Production deploy
npm run build
cat dist/package.json | jq '.n8n' # paths must NOT have a dist/ prefix
cd dist && npm pack
# on the n8n host/volume:
npm install /path/to/n8n-nodes-whatsapp-personal-*.tgz
# restart ALL n8n containers (main + every worker), not just main
Or, once published: npm install n8n-nodes-whatsapp-personal via n8n's
Settings → Community Nodes installer (N8N_COMMUNITY_PACKAGES_ENABLED=true
must be set on the instance).
Run bridge/ on the same host as the n8n instance that calls it (bind127.0.0.1, no API key strictly needed, nothing exposed). Only bind the
bridge beyond loopback if n8n runs elsewhere — and in that case,WA_BRIDGE_API_KEY is not optional.
Known limitations / risks
- WhatsApp Web (Baileys) is an unofficial, reverse-engineered protocol,
not Meta's Cloud API. Automated outbound messages at real volume carry a
real ban risk — fine for low-volume personal/internal alerts, not
recommended as the only channel for anything customer-facing or
business-critical. - The bridge's incoming-message queue lives in memory only (capped at
100, oldest dropped first) and is lost on bridge restart — polling
frequency doesn't fix that; only a persistent queue would. Acceptable for
a personal notification log, not for anything requiring guaranteed
delivery. Get Recentis a drain operation — only one consumer (one scheduled
workflow) should poll it, or they'll steal messages from each other.
Contributing
Issues and PRs welcome — this is a small, focused package (one bridge, one
node) and intentionally stays that way. See src/actions/common/ for the
client/transport/executor pattern new operations should follow.
License
MIT © Diginno
