An n8n WhatsApp bot needs three parts: the WhatsApp Trigger to receive messages from Meta's Cloud API, an AI Agent to write the answer, and the WhatsApp Business Cloud node to send it. Replies are only allowed for 24 hours after the user's last message, and since October 1, 2026 Meta bills them per message after 1,000 free a month per number. The template below handles text and images, keeps memory per phone number and sends one reply per message.
To connect n8n to WhatsApp, use the WhatsApp Trigger node to receive messages from Meta's Cloud API and the WhatsApp Business Cloud node to reply, with an AI Agent node between them writing the answer. You need a Meta app with the WhatsApp product, two n8n credentials, and a plan for the 24-hour customer service window, because free-form replies are only allowed inside it and are now billed per message.
Checked October 2026 against n8n's docs for the WhatsApp Trigger, the WhatsApp Business Cloud node and their credentials, the source of n8n 2.42.6, and Meta's docs on service messages, media, webhooks, pricing and the 2026 pricing updates.
This tutorial belongs to our n8n hub. For the platform rules behind it (numbers, templates, messaging limits), keep the WhatsApp Business API guide open in another tab. Here you build the bot: a working webhook, AI replies with memory, image handling, the window rule, and the cost of each message.
- 01User sends a message
Text, photo or voice note to your business number.
- 02Meta calls your webhook
The Cloud API posts the event to the URL n8n registered.
- 03WhatsApp Trigger
n8n checks Meta's signature and starts the workflow.
- 04Normalise and route
Pick out sender, type and text; fetch the file if it is an image.
- 05AI Agent
Loads this sender's recent history and writes one reply.
- 06Send reply
The WhatsApp Business Cloud node posts it back inside the 24-hour window.
What do you need before you build an n8n WhatsApp bot?
- A Meta developer account and a business portfolio
- A Meta business app with the WhatsApp product added, switched to Live mode with privacy policy and terms URLs
- A phone number: the test number Meta gives the app, or your own number registered on the API
- The App ID and App Secret, for the trigger
- An access token and your WhatsApp Business Account ID, for sending
- An n8n instance on a public HTTPS address, because Meta has to reach the webhook
- A payment method on the WhatsApp Business Account: Meta's docs say service messages stop without one
- An API key for the model that writes the replies
- Opt-in from the people you will message, as Meta's policy requires
The first five items come from n8n's credentials page, which walks through each Meta screen. A local n8n on localhost will not work, since Meta cannot call it; our n8n webhook guide covers public URLs and reverse proxies.
n8n WhatsApp bot tutorial: trigger and credentials
The two WhatsApp nodes use two different credentials, which is where most first attempts stall:
| Credential in n8n | Used by | Fields | Where to find them in Meta |
|---|---|---|---|
| WhatsApp OAuth API | WhatsApp Trigger | Client ID and Client Secret | App settings, Basic: the App ID and App Secret |
| WhatsApp API | WhatsApp Business Cloud node and the media download | Access Token and Business Account ID | WhatsApp, API Setup; for production, a System User token from Business settings |
- 1Create both credentials
Add WhatsApp OAuth API and WhatsApp API in n8n. Expected result: the WhatsApp API credential test passes.
- 2Import the template
Paste the JSON from the next section onto a new canvas. Expected result: ten nodes on the canvas.
- 3Select credentials
OAuth on the trigger, WhatsApp API on the four sending and media nodes, your model key on the chat model. Expected result: no red warnings.
- 4Check the trigger
Trigger On is Messages, and Receive Message Status Updates is added with nothing selected. Expected result: only incoming messages start runs.
- 5Publish
Publishing registers the webhook with Meta; there is no verify token to copy. Expected result: the workflow shows as published.
- 6Send a message
From your own phone, message the business number. Expected result: a reply, and one execution in n8n.
A note on tokens. The token on the API Setup page is fine for a first test. For a bot that has to keep running, Meta's access token docs tell direct developers to use a System User access token, which is long-lived, with the whatsapp_business_messaging and whatsapp_business_management permissions.
What the WhatsApp Trigger outputs
n8n unwraps Meta's webhook and gives you one item per change, with the event name added as field. For a text message the shape is, in Meta's own placeholders:
[
{
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "<BUSINESS_DISPLAY_PHONE_NUMBER>",
"phone_number_id": "<BUSINESS_PHONE_NUMBER_ID>"
},
"contacts": [
{ "profile": { "name": "<WHATSAPP_USER_PROFILE_NAME>" }, "wa_id": "<WHATSAPP_USER_ID>" }
],
"messages": [
{
"from": "<WHATSAPP_USER_PHONE_NUMBER>",
"id": "<WHATSAPP_MESSAGE_ID>",
"timestamp": "<WEBHOOK_TRIGGER_TIMESTAMP>",
"type": "text",
"text": { "body": "<MESSAGE_TEXT_BODY>" }
}
],
"field": "messages"
}
]| What you need | Expression |
|---|---|
| Sender's number | {{ $json.messages[0].from }} |
| Sender's profile name | {{ $json.contacts[0].profile.name }} |
| Message type | {{ $json.messages[0].type }} |
| Text of a text message | {{ $json.messages[0].text.body }} |
| Media ID of an image | {{ $json.messages[0].image.id }} |
| Media ID of a voice note | {{ $json.messages[0].audio.id }} |
| Your phone number ID, needed to reply | {{ $json.metadata.phone_number_id }} |
| Message ID, for de-duplication | {{ $json.messages[0].id }} |
Two things arrive that are not messages. Delivery receipts (sent, delivered, read) come through the same Messages subscription, and by default each one starts an execution. The trigger's Receive Message Status Updates option filters them; the template sets it to none. Meta also says retries "can result in duplicate webhook notifications", so store the message ID if a double reply would hurt.
n8n WhatsApp AI agent template (importable JSON)
Copy this, open a new workflow and paste it onto the canvas. Node types, versions and parameter names come from the n8n 2.42.6 source. Replace the ACME system message with your own business before you publish.
{
"name": "WhatsApp AI reply bot",
"nodes": [
{
"parameters": {
"updates": [
"messages"
],
"options": {
"messageStatusUpdates": []
}
},
"id": "e5c1a7d3-0001-4a6f-9c5e-000000000001",
"name": "WhatsApp Trigger",
"type": "n8n-nodes-base.whatsAppTrigger",
"typeVersion": 1,
"position": [
0,
0
],
"webhookId": "e5c1a7d3-1000-4a6f-9c5e-000000000010"
},
{
"parameters": {
"jsCode": "const value = $input.first().json;\nconst msg = value.messages?.[0];\n\n// Status updates and other events carry no message: stop here.\nif (!msg) return [];\n\n// Meta retries failed webhooks for up to 7 days. A free-form reply is only\n// allowed for 24 hours after the user's message, so drop anything older.\nconst ageHours = (Date.now() / 1000 - Number(msg.timestamp)) / 3600;\nif (ageHours >= 24) return [];\n\nconst media = msg.image ?? msg.audio ?? msg.video ?? msg.document ?? msg.sticker ?? null;\n\nreturn [{\n json: {\n from: msg.from,\n name: value.contacts?.[0]?.profile?.name ?? '',\n phoneNumberId: value.metadata?.phone_number_id ?? '',\n messageId: msg.id,\n type: msg.type,\n text: msg.text?.body ?? msg.image?.caption ?? '',\n mediaId: media?.id ?? '',\n },\n}];"
},
"id": "e5c1a7d3-0002-4a6f-9c5e-000000000002",
"name": "Normalise message",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
220,
0
]
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "e5c1a7d3-2001-4a6f-9c5e-000000000021",
"leftValue": "={{ $json.type }}",
"rightValue": "text",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Text"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "e5c1a7d3-2002-4a6f-9c5e-000000000022",
"leftValue": "={{ $json.type }}",
"rightValue": "image",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Image"
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"id": "e5c1a7d3-0003-4a6f-9c5e-000000000003",
"name": "Route by type",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
440,
0
]
},
{
"parameters": {
"resource": "media",
"operation": "mediaUrlGet",
"mediaGetId": "={{ $json.mediaId }}"
},
"id": "e5c1a7d3-0004-4a6f-9c5e-000000000004",
"name": "Get media URL",
"type": "n8n-nodes-base.whatsApp",
"typeVersion": 1.1,
"position": [
660,
180
]
},
{
"parameters": {
"url": "={{ $json.url }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "whatsAppApi",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
}
},
"id": "e5c1a7d3-0005-4a6f-9c5e-000000000005",
"name": "Download image",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
880,
180
]
},
{
"parameters": {
"promptType": "define",
"text": "={{ $('Normalise message').first().json.text || 'The user sent this image without a caption. Say what you see and ask how you can help.' }}",
"options": {
"systemMessage": "You are the WhatsApp assistant for ACME Plumbing. Reply in the language the user writes in, in under 80 words, in one message. Only answer questions about our services, prices and bookings. If you are not sure, say so and offer to connect a person."
}
},
"id": "e5c1a7d3-0006-4a6f-9c5e-000000000006",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3,
"position": [
1100,
0
]
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "claude-haiku-5-5"
},
"options": {}
},
"id": "e5c1a7d3-0007-4a6f-9c5e-000000000007",
"name": "Anthropic Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
"typeVersion": 1.3,
"position": [
1040,
220
]
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $('Normalise message').first().json.from }}",
"contextWindowLength": 5
},
"id": "e5c1a7d3-0008-4a6f-9c5e-000000000008",
"name": "Simple Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [
1200,
220
]
},
{
"parameters": {
"resource": "message",
"operation": "send",
"messagingProduct": "whatsapp",
"phoneNumberId": "={{ $('Normalise message').first().json.phoneNumberId }}",
"recipientPhoneNumber": "={{ $('Normalise message').first().json.from }}",
"messageType": "text",
"textBody": "={{ $json.output.slice(0, 4096) }}",
"additionalFields": {}
},
"id": "e5c1a7d3-0009-4a6f-9c5e-000000000009",
"name": "Send reply",
"type": "n8n-nodes-base.whatsApp",
"typeVersion": 1.1,
"position": [
1440,
0
]
},
{
"parameters": {
"resource": "message",
"operation": "send",
"messagingProduct": "whatsapp",
"phoneNumberId": "={{ $('Normalise message').first().json.phoneNumberId }}",
"recipientPhoneNumber": "={{ $('Normalise message').first().json.from }}",
"messageType": "text",
"textBody": "I can read text and photos at the moment. Could you type that out for me?",
"additionalFields": {}
},
"id": "e5c1a7d3-0010-4a6f-9c5e-000000000011",
"name": "Send fallback",
"type": "n8n-nodes-base.whatsApp",
"typeVersion": 1.1,
"position": [
660,
380
]
}
],
"connections": {
"WhatsApp Trigger": {
"main": [
[
{
"node": "Normalise message",
"type": "main",
"index": 0
}
]
]
},
"Normalise message": {
"main": [
[
{
"node": "Route by type",
"type": "main",
"index": 0
}
]
]
},
"Route by type": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
],
[
{
"node": "Get media URL",
"type": "main",
"index": 0
}
],
[
{
"node": "Send fallback",
"type": "main",
"index": 0
}
]
]
},
"Get media URL": {
"main": [
[
{
"node": "Download image",
"type": "main",
"index": 0
}
]
]
},
"Download image": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Send reply",
"type": "main",
"index": 0
}
]
]
},
"Anthropic Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}What each part does, and why:
- Normalise message flattens the payload into
from,type,textandmediaId. It stops quietly when an event has no message, and when a message is more than 24 hours old. - Route by type sends text to the agent, images down the download path, and everything else to a polite fallback reply.
- AI Agent uses Claude Haiku 5.5 through the Anthropic Chat Model node; swap in any chat model. The system message asks for one short reply, which matters for cost.
- Simple Memory is keyed by the sender's phone number, so each person has their own conversation. It is lost when n8n restarts; our n8n agent memory guide shows the swap to Postgres.
- Send reply cuts the answer at 4,096 characters, the node's limit for a text body.
One limit to know: the template reads the first message in each webhook delivery. If your executions ever show several messages in one delivery, add a Loop Over Items node after the trigger.
How do you handle images and voice notes?
In two hops, because the webhook carries a media ID and not the file. Meta's media docs set the timings: media IDs in webhooks expire after 7 days, the URL you exchange one for "is only valid for 5 minutes", and the download needs your access token as a Bearer header.
| Message type | What arrives | What the template does |
|---|---|---|
| Text | messages[0].text.body | Goes straight to the AI Agent |
| Image | messages[0].image with an id, a mime_type and an optional caption | Media ID to URL, download the file, pass it to the agent as an image |
| Voice note | messages[0].audio with an id and voice set to true | Gets the fallback reply until you add a transcription step |
| Video, document, sticker, location | An object named after the type | Gets the fallback reply |
In n8n the two hops are two nodes. The WhatsApp Business Cloud node's Media, Download operation is slightly misnamed: in the source it calls GET /<media-id> and returns the URL, not the file. An HTTP Request node then fetches that URL, with Authentication set to Predefined Credential Type and your WhatsApp API credential, and Response Format set to File. The AI Agent passes binary images to the model by default, so a vision-capable model sees the photo with no further wiring.
Voice notes arrive as audio/ogg; codecs=opus with voice set to true. To support them, add an Audio rule to the Switch, reuse the same two download nodes, and put a speech-to-text node of your choice between the download and the agent, passing the transcript in as the prompt. The template sends the fallback reply for audio until you add that.
The 24-hour service window rule
A bot that answers immediately never breaks this rule; a bot that follows up later breaks it all the time. Meta's wording: when a user messages you, "a 24-hour timer called a customer service window starts", each new user message resets it, and "when the window closes, you can only send pre-approved template messages".
- Hour 0The user messages you
The window opens. Free-form replies of any type are allowed.
- Hour 0 to 24Your bot replies
Each reply is a service message. This is where the template works.
- Any timeThe user writes again
The timer resets to 24 hours from that message.
- Hour 24The window closes
Free-form sends fail with error 131047. Only approved templates go through.
- LaterYou want to follow up
Use the node's Send Template operation with a template Meta has approved.
Source: Meta send messages and error code docs, checked October 2026
Three places the rule catches n8n builders:
- Outages. Meta retries failed webhook deliveries "for up to 7 days". After downtime, n8n receives old messages whose window has closed. The Code node drops anything older than 24 hours instead of firing replies that will fail.
- Human handoff. If a person answers the next morning, the window may be shut. Have the handoff path check the time of the user's last message, and fall back to a template.
- Reminders and nurture. Anything you start is a template message, charged by category. That is a separate workflow with a Schedule Trigger, not part of this bot.
What does each WhatsApp bot message cost?
Two meters run on every reply: Meta's delivery charge and your AI provider's tokens. Meta's pricing docs state that, effective October 1, 2026, service messages are charged per delivered message at the same rate as utility messages in each market, with no volume tiers, and that every business phone number gets 1,000 free service messages a month. Messages users send you are not charged.
| Replies in a month | Meta delivery | AI tokens | Total |
|---|---|---|---|
| 1,000 | $0.00 (inside the free tier) | $0.16 | $0.16 |
| 5,000 | $27.20 | $0.80 | $28.00 |
| 20,000 | $129.20 | $3.20 | $132.40 |
- Delivery dominates. Past the free tier, a reply costs about 0.70 cents in this example, and 0.68 of that is Meta. A larger model changes the picture: the same reply on Claude Sonnet 5.5 costs 0.32 cents in tokens.
- One reply per message. Meta charges per delivered message. An agent that splits its answer into three bubbles pays three delivery charges. The system message in the template asks for one.
- Ads open a free window. Meta's docs keep service messages free for 72 hours when the chat starts from a Click to WhatsApp ad or a Facebook Page button.
- Meta sells its own agent. Replies written by Meta Business Agent are billed at $2.00 per million tokens, which Meta puts at about 4 to 5 cents a message. In the same document Meta estimates third-party AI at about 2 to 9 cents a message; your real figure depends on the model and prompt size.
Each incoming message is also one n8n execution; our n8n pricing guide shows what that means per plan. If you are turning a bot like this into something clients pay for, the AI SaaS Builder program covers pricing from unit costs like these, and the Stripe billing to charge for it.
Policy and troubleshooting
Two policy points decide whether the bot is allowed at all. First, Meta's AI Providers page says that since January 15, 2026 general-purpose AI assistants may only be offered on the platform where Meta is legally required to permit them. A bot that answers for your own business is the ordinary case; a "chat with an AI about anything" number is not. Second, you may only message people who opted in, and you should always offer a way to reach a person.
- A general-purpose assistant on a business number
- Free-form messages after the window has closed
- Several reply bubbles per question
- No route to a human
- A test token in production
- A bot scoped to your own products and bookings
- Templates for anything you start
- One short reply per user message
- A handoff keyword that alerts your team
- A System User token and a payment method on file
- No executions at all. The workflow is not published, or the app is still in Development mode. The n8n credentials docs list switching App Mode to Live as a setup step.
- "Already has a webhook subscription". Another trigger or tool owns the app's webhook. Remove it or use a separate Meta app.
- Extra executions after every reply. Status updates are getting through. Set Receive Message Status Updates to none.
- Replies fail with 131047. The window is closed. Send a template.
- Image download returns 404. The media URL expired. Meta's docs say to request a new URL from the media ID and try again.
- Replies stopped being delivered in October 2026. Check the payment method on the WhatsApp Business Account.
A bot that talks to customers should tell you when it breaks. Attach an error workflow before launch; the n8n Error Trigger guide has one you can import. If you would rather not build the inbox side yourself, our comparison of WhatsApp Business API providers covers the ready-made options.
n8n WhatsApp bot: FAQ
How do I connect n8n to WhatsApp?
Create a Meta app with the WhatsApp product, then add two credentials in n8n. The WhatsApp Trigger uses the app's App ID and App Secret, and the WhatsApp Business Cloud node uses an access token plus your WhatsApp Business Account ID. Set the trigger to the Messages event, publish the workflow, and n8n registers the webhook with Meta for you. Checked October 2026.
Is the WhatsApp Cloud API free to use with n8n?
Access is free, messages are not always. Meta's pricing documentation says that from October 1, 2026 free-form replies, called service messages, are charged per delivered message at the same rate as utility messages in each market, after 1,000 free service messages per business phone number each month. Template messages are charged by category. You also pay your AI provider for each reply.
What is the 24-hour rule for WhatsApp bots?
When a user messages your number, a 24-hour customer service window opens and resets with each new message from them. Inside it your bot can send free-form replies. Once it closes you can only send pre-approved template messages, and a free-form message fails with error 131047. A bot that replies straight away is always inside the window; follow-ups the next day are not.
Can an n8n WhatsApp bot read images and voice notes?
Yes, in two steps. The webhook carries a media ID, not the file. Use the WhatsApp Business Cloud node's Media Download operation to turn the ID into a URL, which Meta says expires after five minutes, then an HTTP Request node with the same credential to fetch the file. Images can go straight to a vision model; voice notes need a transcription step first.
Why does my n8n WhatsApp Trigger only work in test or in production?
Meta allows one webhook per app. n8n's docs explain that switching between the test URL and the production URL overwrites the registered address, so only one of them receives events at a time. Unpublish the workflow while you test, then publish it again. For the same reason, one Meta app can serve only one WhatsApp Trigger.
Can I run a ChatGPT-style assistant on WhatsApp?
Not as a general-purpose assistant. Meta's documentation says that since January 15, 2026 providers of general-purpose AI assistants may only offer them on the WhatsApp Business Platform where Meta is legally required to allow it. A bot that uses AI to serve your own customers, such as answering questions about your products or bookings, is the normal service-message use case.
You have a bot that answers customers. Now make it a product.
AI SaaS Builder, included in All Access, covers the steps after the workflow: validating who would pay, pricing from your unit costs, Stripe billing and launch. All Access adds the other three programs, live coaching and the private community.
Start with the free Creator Starter Kit
Templates and checklists for your first automations, free.