Skip to main content
Version: v0.23.1

Build WhatsApp workflows with n8n

Connect OpenWA to n8n, an open-source workflow automation tool, using the official community nodes. By the end of this page you'll have the nodes installed, an OpenWA credential configured, and a working auto-reply workflow: an incoming WhatsApp message triggers a reply.

The package ships two nodes:

NodeTypeWhat it does
OpenWAActionCalls OpenWA operations as a workflow step — send messages, manage sessions, chats, contacts, groups, labels, templates, status, channels, and webhooks
OpenWA TriggerTriggerStarts a workflow when a WhatsApp event arrives — messages, session, group, call, and presence events
Prerequisites
  • A running OpenWA gateway that n8n can reach over the network. See Installation.
  • A connected session that has finished pairing. See Your first session.
  • An API key from the dashboard. See the Authentication guide.
  • An n8n instance where you can install community nodes (self-hosted).
  • An OpenWA server ≥ 0.14.0. The nodes are verified against v0.23.1.
The server floor is checked when the webhook is registered

Package 0.9.1 raised the required server from 0.10.9 to 0.14.0. Five of the events the Trigger offers — session.restriction, presence.update, call.accepted, call.rejected, and call.missed — do not exist in core before v0.14.0, and the gateway validates a webhook registration against its own event list. A Trigger subscribing to any of them against an older gateway is refused at registration with a 400, so the workflow fails to activate rather than activating and quietly never firing.

Install the nodes

Install the community package on a self-hosted n8n instance.

From the n8n UI

  1. In n8n, open Settings → Community Nodes.
  2. Click Install.
  3. Enter @rmyndharis/n8n-nodes-openwa and accept the community-node risk prompt.
  4. Restart n8n.

Manual install

cd ~/.n8n/nodes
npm install @rmyndharis/n8n-nodes-openwa

Restart n8n after installing. Both nodes then appear in the node picker when you search for "OpenWA".

n8n Cloud

n8n Cloud only allows community nodes verified by n8n, and this package is not currently one of them. Use a self-hosted instance to run it.

Configure the OpenWA API credential

Both nodes share one credential. Create it once and reuse it across every OpenWA node in your instance.

FieldRequiredDefaultDescription
Server URLYeshttp://localhost:2785Your OpenWA server URL, without a trailing slash or /api. Use HTTPS in production (for example https://wa.yourserver.com).
API KeyYesThe API key from your OpenWA dashboard. The node sends it as the X-API-Key header on every request.

When you click Test in the credential dialog, n8n makes an authenticated GET /api/sessions request to your server. The test passes only if the key is valid, so a wrong key or unreachable server fails immediately.

Use a scoped key

Create a dedicated API key for n8n rather than reusing an admin key, so you can revoke it without disrupting other clients. See the Authentication guide.

Most reads work with a plain VIEWER key. Writes — sends, group changes, profile changes, and webhook management — need an OPERATOR-role key (the default role). The whole API Key resource, plus System → Get Settings, System → Get Audit Log, and Webhook → Get Delivery Failures, needs an ADMIN key. A 403 almost always means the key's role is too low, not that the request was malformed.

You can confirm the same credential by hand with curl before building a workflow. This hits the same send endpoint the Send Text operation uses, so a success here confirms both the credential and a connected session:

curl -X POST http://localhost:2785/api/sessions/8f3c2b1a-9d4e-4c7a-8b2f-1e6d5a4c3b2a/messages/send-text \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"chatId": "628123456789@c.us",
"text": "Hello from OpenWA!"
}'

On success the gateway returns 201 Created with the message id and timestamp:

{
"messageId": "true_628123456789@c.us_3EB0123456789",
"timestamp": 1706868000
}

A 404 means the session id doesn't exist; a 400 means the session isn't connected or the body is invalid. Replace the UUID with your own session id — the id from the create response, not the name you gave it — and 628123456789@c.us with a real recipient chat id.

The OpenWA node (actions)

The action node maps a Resource and Operation to one OpenWA call. Package 0.9.0 extended it from four resources to the fifteen below, covering the usable OpenWA API surface. For field-level detail of every operation, see the n8n Node Reference.

ResourceOperationsWhat it covers
SessionCreate, Start, Stop, Force Kill, Delete, Get QR, Request Pairing Code, Get Status, List All, Get Stats OverviewSession lifecycle and pairing
MessageSend Text / Image / Video / Audio / Document / Sticker / Location / Contact / Poll / Template, Reply, React, Edit, Delete, Forward, Send Bulk, Get Batch Status, Cancel Batch, List, Get History, Get ReactionsEverything that sends or acts on a message
ContactCheck Exists, Get Info, Get Phone, Get Profile Picture, Get Profile Pictures, List, Block, UnblockContact lookup and blocking
ChatList, Mark Read, Mark Unread, Delete, Set StateConversations, and typing or recording indicators
GroupList, Get, Create, Join, Leave, Add / Remove / Promote / Demote Participants, Update Subject, Update Description, Get Settings, Update Settings, Get Invite Code, Revoke Invite CodeGroup administration
ProfileSet Name, Set Status, Set PictureThe session's own WhatsApp profile
LabelList, Get, Get for Chat, Add to Chat, Remove From ChatWhatsApp Business labels
StatusList, Get by Contact, Get Media, Delete, Send Text, Send Image, Send VideoReading and posting Status (Stories)
TemplateList, Get, Create, Update, DeleteStored templates with {{variable}} placeholders
ChannelList, Get, Get Messages, Subscribe, UnsubscribeWhatsApp Channels the session follows
CallRejectDeclining an incoming call
ObservabilityCheck, Check Liveness, Check ReadinessServer health probes
SystemGet Settings, Get Stats Overview, Get Message Stats, Get Session Stats, Get Audit Log, SearchServer-wide reporting and cross-session search
API KeyList, Get, Create, Update, Revoke, Delete, ValidateAPI-key administration
WebhookCreate, Get, List, List All, Update, Delete, Test, Get Delivery FailuresWebhook registration and delivery inspection

See the n8n Node Reference for the full field-level detail of every operation. A few endpoints are deliberately absent because the server cannot serve them: Send Catalog and PUT /api/settings were removed from the server in v0.19.0 (both always answered 501); catalog reads and Send Product are Baileys-only and have no operations here — call them with n8n's generic HTTP Request node against a Baileys session if you need them; settings are environment-derived and read-only, and /api/metrics authenticates with its own bearer token rather than the X-API-Key this credential carries. Presence subscription (POST /api/sessions/{sessionId}/presence/subscribe) has no operation either — call it with n8n's generic HTTP Request node using the same credential if you want presence.update events. The complete endpoint list is in the API reference.

Base64 media needs a MIME type

When you send an image or document from a Base64 source, also set the MIME Type field (for example image/png or application/pdf) — OpenWA requires it for base64 payloads. The Binary source fills this in from the binary metadata, and the URL source needs nothing extra.

The OpenWA Trigger node (events)

The trigger node starts a workflow when a WhatsApp event occurs. When you activate the workflow, the node registers a webhook in OpenWA pointing at n8n's webhook URL; when you deactivate it, that webhook is removed. You never manage the webhook by hand.

Select one or more events to subscribe to. Package 0.9.1 offers all twenty-two events the gateway dispatches:

EventDescription
message.receivedNew incoming message
message.sentMessage successfully sent
message.ackMessage delivery or read acknowledgement
message.failedMessage failed to send
message.revokedMessage deleted for everyone
message.editedMessage text or media edited
message.reactionReaction added to or removed from a message
status.receivedA contact's Status (Story) is received
session.statusSession status changed
session.qrQR code generated for scanning
session.authenticatedSession authenticated
session.disconnectedSession lost connection
session.reconnect_loopSession stuck in a reconnect loop (once per 5 consecutive attempts)
session.restrictionWhatsApp places or lifts a restriction on the account
presence.updateA subscribed chat's presence changes — Baileys only
group.joinA participant joins a group the session belongs to
group.leaveA participant leaves a group the session belongs to
group.updateA group's metadata or roster changes
call.receivedAn incoming WhatsApp call is detected
call.acceptedAn incoming call is answered — Baileys only
call.rejectedAn incoming call is declined, including by auto-reject — Baileys only
call.missedAn incoming call goes unanswered — Baileys only

Every event above is available on any server this package supports, so there are no per-event version caveats left to track — the ≥ 0.14.0 floor covers them all.

Four events fire on one engine only

The three call-outcome events fire on Baileys only. whatsapp-web.js hooks the call collection's insert and sees no status at all, so it can report the ring but never how the call ended — a workflow triggered on call.missed simply never runs on a whatsapp-web.js session. call.received fires on both engines.

presence.update is also Baileys-only, and it stays silent until you subscribe the chat with POST /api/sessions/{sessionId}/presence/subscribe. whatsapp-web.js answers that route with 501.

Signature verification

The trigger has an optional Webhook Secret. When set, the secret is registered with OpenWA, and OpenWA signs every delivery with HMAC-SHA256 in the X-OpenWA-Signature: sha256=<hex> header. The node verifies each delivery against the raw request body and rejects any that fail with HTTP 401. Leave it empty to skip verification.

note

Changing or clearing the secret takes effect on the next activation. Deactivate and reactivate the workflow to re-register it.

Trigger payload

The trigger emits one item per event. The event is wrapped in an envelope; the actual event data lives under data:

{
"event": "message.received",
"timestamp": "2024-01-15T10:30:00Z",
"sessionId": "default",
"idempotencyKey": "a1b2c3d4e5f6",
"deliveryId": "9f8e7d6c5b4a",
"data": {
"id": "3EB0F5A2B4C1",
"chatId": "628123456789@c.us",
"from": "628123456789@c.us",
"body": "Hello!",
"type": "text",
"timestamp": 1705312200
}
}

Reference fields in downstream nodes with n8n expressions — for example {{$json.data.body}} for the message text and {{$json.data.chatId}} for the chat to reply to.

A few notes from the payload contract:

  • Read message fields from data (data.body, data.chatId), and the message identifier from data.id — incoming payloads use id, not messageId.
  • type is engine-neutral: voice notes are voice, shared contacts are contact, and plain chats are text.
  • Check Exists returns whatsappId, the engine-canonical chat id, which may differ from the number you sent (for example an @lid id).
Deduplicate retries

OpenWA retries failed deliveries with the same deliveryId. If your downstream actions aren't idempotent, add a dedup step (a Remove Duplicates node or an IF check) keyed on deliveryId so a retried delivery isn't processed twice.

Example: auto-reply to incoming messages

This is the smallest useful workflow — trigger on an incoming message, check the text, and reply.

  1. OpenWA Trigger — select your OpenWA API credential and subscribe to message.received.
  2. IF — condition: {{$json.data.body}} contains hello (case-insensitive).
  3. OpenWA → Send Text — on the true branch, configure:
    • Session Name or ID: pick your session from the dropdown, which lists the sessions on your server
    • Chat ID: {{$json.data.chatId}}
    • Message: Hi! Thanks for reaching out — how can we help?
  4. Activate the workflow. The trigger registers its webhook with OpenWA automatically.

Send "hello" to your WhatsApp number, and you'll get the reply within seconds.

From here, swap the IF for a router to branch on keywords, append each new lead to Google Sheets before replying, or post a Slack alert when session.disconnected fires. The trigger gives you the inbound event; the action node sends the response.

Troubleshooting

SymptomCauseFix
Credential Test failsServer unreachable, wrong key, or a trailing slash in Server URLConfirm the server is running and reachable from n8n, the key is correct, and Server URL has no trailing / or /api.
Trigger fires once in the editor, then goes quietListen for test event registers n8n's test URL, which stops after a single deliveryActivate the workflow so the trigger registers its production URL.
Trigger never firesOpenWA can't reach n8n's webhook URLConfirm n8n's webhook URL is reachable from the gateway (check firewalls and proxies) and that the session is connected. Then ask the gateway which side dropped the event with GET /api/webhooks/delivery-failures?sessionId={sessionId} (ADMIN key): a row means OpenWA delivered and n8n rejected it, an empty list means the event never reached delivery.
A call.accepted, call.rejected, or call.missed trigger never runsThe session runs whatsapp-web.jsThose three outcomes are Baileys-only. Use call.received, which fires on both engines, or move the session to Baileys.
Workflow won't activate; webhook creation fails with 400The gateway doesn't know one of the selected eventssession.restriction, presence.update, and the three call outcomes need OpenWA ≥ 0.14.0. Upgrade the gateway, or deselect the events it doesn't know.
Send Text returns 400Session not connected, or invalid chat idConfirm the session status is ready and the chat id format is correct (628123456789@c.us).
Send Text returns 404Session id doesn't existPick a real session from the node's Session Name or ID dropdown.
Message sends but never arrivesRecipient isn't on WhatsAppUse the Check Exists operation before sending to unverified numbers.

Next steps