whatsapp-personal

n8n community node to send/receive WhatsApp messages from a personal number via a self-hosted Baileys bridge — no Meta Business API required. Built by Diginno.

Package Information

Released: 3 mo ago
Downloads: 0 weekly / 0 monthly
Latest Version: 0.2.0

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 (default
http://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 (bind
127.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 Recent is 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

Discussion