Automate Lead Follow-Up With WhatsApp Business API, Google Sheets and Next.js (n8n, Step by Step)
Marwan Ayman
Full-Stack Developer & Automation Engineer

Short answer: the fastest reliable way to follow up leads on WhatsApp is a five-node n8n workflow: a webhook receives the lead from your Next.js form, a Google Sheets node logs and deduplicates it, an HTTP Request node sends an approved WhatsApp template through Meta's Cloud API within seconds, a second webhook routes replies to a salesperson, and an error workflow alerts you when anything fails. It runs on a $5 server, costs Meta's per-conversation fee, and takes two to five days to build properly.
This is the workflow I deploy most often for businesses in Egypt and the Gulf, where a lead that gets a WhatsApp message within a minute converts far better than one that gets an email tomorrow. Below is the exact node list, the payloads, and the compliance rules that decide whether Meta lets the messages through.
What does the finished workflow look like?
| # | n8n node | What it does | Gotcha |
|---|---|---|---|
| 1 | Webhook (POST /lead) | Receives the lead from the Next.js server action | Require a shared secret header; respond immediately, do the work after |
| 2 | Code or Set | Normalises the phone number to E.164 (+2010..., +9665...) | Egyptian numbers typed as 010… need the country code added; strip spaces and zeros |
| 3 | Google Sheets: Append or Update | Writes the lead, matching on phone to prevent duplicates | Use a dedicated column as the match key; Sheets is not a database |
| 4 | IF (is new lead?) | Only new leads get the first message | Returning leads go to the sales notification branch instead |
| 5 | HTTP Request → WhatsApp Cloud API | Sends the approved template | Templates only; free-form text fails outside the 24-hour window |
| 6 | Slack / Email / WhatsApp to sales | Tells a human a lead arrived, with the sheet row link | Include the lead's language so the right rep replies |
| 7 | Webhook (Meta callback) | Receives delivery status and replies | Must answer Meta's GET verification challenge once |
| 8 | Error Workflow | Posts failures with the execution link | Set it in the workflow settings; retries on the HTTP node with backoff |
How does the Next.js form send the lead to n8n?
Short answer: from a Server Action, never from the browser, so the webhook URL and secret stay on the server and the request can be validated with Zod first.
// app/contact/actions.ts
'use server';
import { z } from 'zod';
const Lead = z.object({
name: z.string().min(2),
phone: z.string().min(8),
message: z.string().max(1000).optional(),
locale: z.enum(['en', 'ar']),
source: z.string().default('website'),
});
export async function submitLead(formData: FormData) {
const lead = Lead.parse(Object.fromEntries(formData));
const res = await fetch(process.env.N8N_LEAD_WEBHOOK!, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-webhook-secret': process.env.N8N_WEBHOOK_SECRET!,
},
body: JSON.stringify({ ...lead, receivedAt: new Date().toISOString() }),
});
if (!res.ok) throw new Error('Lead delivery failed');
return { ok: true };
}In the n8n Webhook node, set the response mode to "Immediately" and add an IF node that checks headers['x-webhook-secret'] against the stored secret before anything else runs. A public webhook without a secret will receive spam within a week.
How do you log and deduplicate leads in Google Sheets?
Short answer: use the Google Sheets node in "Append or Update" mode with the phone number as the column to match on. The sheet becomes the shared log the sales team already knows how to read, and the workflow can tell new leads from returning ones.
Columns that have proven useful: phone, name, locale, source, first_seen, last_seen, status (new, contacted, replied, qualified, lost), owner, wa_message_id, notes. Normalise the phone first so that "010 1234 5678" and "+201012345678" are the same lead.
// Code node: normalise to E.164 (Egypt and Saudi examples)
const raw = String($json.phone).replace(/[^\d+]/g, '');
let phone = raw;
if (/^0?1\d{9}$/.test(raw)) phone = '+20' + raw.replace(/^0/, ''); // Egypt mobile
else if (/^0?5\d{8}$/.test(raw)) phone = '+966' + raw.replace(/^0/, ''); // Saudi mobile
else if (!raw.startsWith('+')) phone = '+' + raw;
return [{ json: { ...$json, phone } }];How do you send the WhatsApp template from n8n?
Short answer: with an HTTP Request node calling Meta's Cloud API /messages endpoint using a permanent system-user token, sending a pre-approved template with the lead's name as a parameter. n8n also ships a WhatsApp Business Cloud node that wraps the same call.
POST https://graph.facebook.com/v21.0/{PHONE_NUMBER_ID}/messages
Authorization: Bearer {PERMANENT_TOKEN}
Content-Type: application/json
{
"messaging_product": "whatsapp",
"to": "{{ $json.phone.replace('+', '') }}",
"type": "template",
"template": {
"name": "lead_followup_v1",
"language": { "code": "{{ $json.locale === 'ar' ? 'ar' : 'en' }}" },
"components": [
{ "type": "body", "parameters": [ { "type": "text", "text": "{{ $json.name }}" } ] }
]
}
}Replace v21.0 with the current Graph API version. Store the returned messages[0].id back in the sheet; it is how you match delivery receipts and replies later. Use a system-user token generated in Meta Business Manager rather than a temporary token from the developer console, which expires in 24 hours and takes the whole workflow down with it.
Which Meta rules decide whether your messages are delivered?
- Opt-in. The lead must have agreed to receive WhatsApp messages. A checkbox or a sentence next to the form's submit button ("We will contact you on WhatsApp") is the usual implementation. Keep the timestamp.
- Templates for business-initiated messages. The first message must be an approved template. Free-form text is only allowed inside the 24-hour window after the customer's last message.
- Template category. Utility templates (confirmations, follow-ups about a request the person made) are cheaper and approved faster than marketing templates. Write the first follow-up as a utility message about their enquiry, not as a promotion.
- Quality rating. Blocks and reports lower your number's quality and messaging limits. Send one follow-up, then stop unless they reply.
- Business verification. Required before scaling past the starter limits. Start it on day one because it can take days.
- Pricing. Meta bills per conversation, with rates that vary by the recipient's country and the template category. Check the current rate card for Egypt, Saudi Arabia or the UAE when you budget; the n8n side costs only the server.
How do replies reach a salesperson?
Short answer: through a second n8n webhook registered as the Meta callback URL. It answers Meta's one-time verification challenge, then receives status updates and inbound messages. Inbound messages update the sheet row to "replied" and notify the owner with the text of the reply.
// Code node after the Meta callback webhook
const entry = $json.body.entry?.[0]?.changes?.[0]?.value;
const msg = entry?.messages?.[0];
const status = entry?.statuses?.[0];
if (msg) return [{ json: { kind: 'reply', from: '+' + msg.from, text: msg.text?.body ?? '', id: msg.id } }];
if (status) return [{ json: { kind: 'status', id: status.id, status: status.status } }];
return [];A reply opens the 24-hour customer-service window, so the salesperson can answer from the WhatsApp Business app or, if you route everything through the API, from your own inbox. For most small teams the app is enough; the automation's job is to guarantee the first minute, not to replace the humans.
What goes wrong, and how do you catch it?
- Template rejected or paused. The HTTP node returns an error code; the error workflow posts it to Slack with the execution link.
- Wrong country code. Silently undelivered. The normalisation step and a test suite of ten real-looking numbers prevent it.
- Token expired. Everything fails at once. Use a permanent system-user token and an uptime check that sends a test template weekly.
- Sheets rate limits. Fine for hundreds of leads a day; move the log to PostgreSQL when you pass that.
- Duplicate sends. Enable "Retry on fail" only on idempotent nodes; the WhatsApp send is not idempotent, so check for an existing
wa_message_idbefore sending.
How much does it cost and how long does it take?
Two to five working days for the complete workflow with error handling, tests and documentation, once templates are approved. On my pricing page an automation workflow is $300 to $2,500 depending on integrations; a version with a CRM upsert and reply routing sits in the middle of that range. Running costs are a $5 to $10 server for self-hosted n8n plus Meta's per-conversation fees.
Frequently asked questions
Do I need Meta business verification to send WhatsApp messages from n8n?
You can test with the Cloud API immediately using a test number, but sending to real customers at volume requires a verified Meta Business account, a registered phone number and approved message templates. Verification usually takes days, so start it before the build.
Can I send any message I want to a new lead on WhatsApp?
No. A business may only start a conversation with an approved template, and only to people who opted in. Once the lead replies, you have a 24-hour customer-service window in which free-form messages are allowed. Marketing templates are priced higher than utility templates and are more often rejected.
Why use Google Sheets instead of a CRM?
Because the sales team already lives in it. The sheet is the shared, visible log; the CRM upsert is a later step in the same workflow. Starting with the sheet gets the automation live in a day and lets you add HubSpot, Zoho or a custom CRM without changing the form.
What does this automation cost to run?
Self-hosted n8n costs about $5 to $10 a month for the server. Meta charges per conversation, with rates that depend on the recipient's country and the template category; check Meta's current rate card for your market. Google Sheets and the Next.js form are free at this scale.
How long does it take to build?
Two to five working days for the workflow described here including error handling and testing, once WhatsApp templates are approved. As a fixed-price automation it typically falls in the $300 to $2,500 range.
Related: hiring a WhatsApp API integration developer and the business automation service.


