Google Sheets to Gmail and WhatsApp, no duplicates
Bulk outreach falls apart the moment you start “just sending a few messages” from a spreadsheet. One bad filter, one accidental re-run, and you’ve double-messaged leads, confused prospects, and lost track of what actually went out.
This is the kind of problem marketing managers run into first. But agency owners and ops-focused founders feel it too. A solid sheets outreach automation gives you a clean system: send in bulk, skip duplicates, and log every result without babysitting the process.
This workflow uses Google Sheets as the control center, sends Gmail and WhatsApp independently, then writes “sent” and “failed” statuses back so you can retry safely and stay organized.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Google Sheets to Gmail and WhatsApp, no duplicates
flowchart LR
subgraph sg0["Manual Flow"]
direction LR
n0@{ icon: "mdi:play-circle", form: "rounded", label: "Manual Trigger", pos: "b", h: 48 }
n1@{ icon: "mdi:swap-vertical", form: "rounded", label: "Split In Batches", pos: "b", h: 48 }
n2@{ icon: "mdi:message-outline", form: "rounded", label: "Send Gmail", pos: "b", h: 48 }
n3@{ icon: "mdi:database", form: "rounded", label: "Whatsapp Failure", pos: "b", h: 48 }
n4@{ icon: "mdi:database", form: "rounded", label: "Mail Failure", pos: "b", h: 48 }
n5@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Sent", pos: "b", h: 48 }
n6@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Delivered", pos: "b", h: 48 }
n7@{ icon: "mdi:database", form: "rounded", label: "Get Contacts", pos: "b", h: 48 }
n8@{ icon: "mdi:database", form: "rounded", label: "Update Sheet", pos: "b", h: 48 }
n9["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/whatsapp.svg' width='40' height='40' /></div><br/>Send template"]
n10@{ icon: "mdi:database", form: "rounded", label: "PrepareEmail email", pos: "b", h: 48 }
n11@{ icon: "mdi:swap-horizontal", form: "rounded", label: "IF Mail Pending", pos: "b", h: 48 }
n12@{ icon: "mdi:swap-horizontal", form: "rounded", label: "IF WhatsApp Pending", pos: "b", h: 48 }
n13@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Has Phone Number", pos: "b", h: 48 }
n14@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Has Email Address", pos: "b", h: 48 }
n15@{ icon: "mdi:swap-vertical", form: "rounded", label: "Build HTML Email", pos: "b", h: 48 }
n16@{ icon: "mdi:cog", form: "rounded", label: "Fetch Email Image", pos: "b", h: 48 }
n5 --> n8
n5 --> n3
n6 --> n8
n6 --> n4
n2 --> n6
n7 --> n1
n8 --> n1
n9 --> n5
n0 --> n7
n11 --> n10
n15 --> n16
n13 --> n12
n1 --> n13
n1 --> n14
n16 --> n2
n14 --> n11
n10 --> n15
n12 --> n9
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n0 trigger
class n5,n6,n11,n12,n13,n14 decision
class n3,n4,n7,n8,n10 database
classDef customIcon fill:none,stroke:none
class n9 customIcon
The Problem: Bulk Outreach Creates Duplicates and Guesswork
Most spreadsheet-based outreach starts with good intentions: “We’ll track everything in one place.” Then reality hits. Someone sorts a column, another person copies rows into a new tab, and your status cells stop matching what was actually sent. Gmail sends, but you don’t know who received it. WhatsApp fails, but the team can’t tell if it failed or simply never ran. A week later you “retry,” and people who already got your message get it again. Honestly, that’s how brands get muted.
The friction compounds. Here’s where it breaks down.
- Re-running a bulk send without channel-level checks leads to accidental duplicates, which is the fastest way to burn a list.
- Status tracking drifts when delivery results live in inboxes instead of back in the sheet where the team actually works.
- Throughput gets messy because sending too fast can trigger rate limits, while sending too slowly turns “today’s campaign” into next week’s problem.
- When email and WhatsApp are tied together, one missing field (like a phone number) can derail the whole run instead of just skipping that channel.
The Solution: Send Gmail + WhatsApp From Sheets, Safely
This workflow turns your Google Sheet into a controlled sending queue. You start it manually, n8n pulls your contacts, then processes them in small batches so you don’t overwhelm Gmail or WhatsApp. For each contact, it checks the basics (email present, phone present) and then checks something more important: the channel status. If the email status is still “pending,” it builds the email from a template, assembles the final HTML, optionally pulls an image from Google Drive, and sends through Gmail. Separately, if WhatsApp is “pending,” it sends an approved template message. Every success or failure is written back to Google Sheets, so your sheet stays truthful and retries don’t create duplicates.
The workflow begins with a manual trigger, grabs rows from Google Sheets, and splits them into manageable batches. Then it runs two independent delivery paths (Gmail and WhatsApp) based on each row’s fields and “pending” flags. Finally, it updates your sheet with sent/failed outcomes so you can safely re-run the workflow whenever you need.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you have 200 contacts in a Google Sheet and you want to reach each one on two channels. Manually, even a “fast” process is maybe 1 minute to send an email, then another minute to send a WhatsApp message, plus time to mark statuses, so you’re looking at roughly 6–7 hours of focused work. With this automation, you start the run once (about 5 minutes), n8n batches the sends in the background, and your sheet updates itself with sent/failed results. You still follow up like a human, but you stop doing the clerical work.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets to store contacts and statuses.
- Gmail to send bulk emails from your account.
- WhatsApp (template messaging provider) to send approved template messages.
- InboxPlus to load and render your email template.
- Google Drive to fetch images used in emails.
Skill level: Intermediate. You’ll connect accounts, map sheet columns to fields, and confirm your “pending/sent/failed” status logic.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
You start the run on purpose. The workflow uses a manual trigger, which is handy for outreach because you typically want a deliberate “go” moment (not an always-on robot).
Contacts are pulled from Google Sheets and paced. n8n reads your rows, then splits them into batches so sending happens in small, predictable chunks instead of a single spike that can cause rate limiting.
Email and WhatsApp are handled as separate lanes. If a contact has an email and the email status is still pending, the workflow loads your InboxPlus template, assembles the final HTML, optionally downloads an image from Google Drive, and sends via Gmail. If a contact has a phone number and WhatsApp is pending, it sends the pre-approved WhatsApp template message.
Everything is written back to the sheet. Successful sends update your “status” columns, and failures are logged too, so you can retry only what failed and avoid re-sending what already worked.
You can easily modify the status names and routing rules to match your team’s process. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Manual Trigger
This workflow starts manually so you can test outreach batches on demand.
- Add the Manual Start node as the trigger.
- Click Execute Workflow later to run the outreach flow.
Step 2: Connect Google Sheets
Pull contact data and update outreach status in Google Sheets.
- Open Retrieve Contacts and select your Google Sheets file and sheet. Set documentId to
[YOUR_ID]and sheetName to[YOUR_ID]. - Credential Required: Connect your googleSheetsOAuth2Api credentials in Retrieve Contacts.
- In Update Status Sheet, set operation to
updateand map Phone Number to{{ $('Retrieve Contacts').item.json['Phone Number'] }}. - Credential Required: Connect your googleSheetsOAuth2Api credentials in Update Status Sheet.
- For failure logging, configure Log WhatsApp Failure and Log Email Failure with operation set to
updateand map Phone Number to{{$json['Phone Number']}}. - Credential Required: Connect your googleSheetsOAuth2Api credentials in Log WhatsApp Failure and Log Email Failure.
Phone Number correctly.Step 3: Configure Batching and Parallel Validation
Split contacts into batches and validate phone/email in parallel before sending.
- In Batch Divider, set batchSize to
3. - Configure Validate Phone Present with a notEmpty condition on
{{ $json['Phone Number'] }}. - Configure Validate Email Present with a notEmpty condition on
{{ $('Batch Divider').item.json.Email }}. - Batch Divider outputs to both Validate Phone Present and Validate Email Present in parallel.
{{ $('Batch Divider').item.json.Email }}.Step 4: Set Up WhatsApp Outreach and Status Updates
Send WhatsApp templates only when the message is pending and a phone number exists.
- In Check WhatsApp Pending, set the condition to
{{ $json['Message Sent'] }}equalsPending. - Configure Dispatch WhatsApp Template with template set to
hello_world|en_US, phoneNumberId to[YOUR_ID], and recipientPhoneNumber to{{ String($json['Phone Number']) }}. - Credential Required: Connect your whatsAppApi credentials in Dispatch WhatsApp Template.
- In Confirm WhatsApp Sent, check if
{{ $json.messages[0].message_status }}equalsaccepted. The true path updates Update Status Sheet, and the false path logs to Log WhatsApp Failure.
Step 5: Set Up Email Creation, Assets, and Delivery
Generate a personalized HTML email, attach assets, and verify successful delivery.
- In Check Email Pending, set the condition to
{{ $json['Mail Sent'] }}equalsPending. - Configure Compose InboxPlus Email with templateId set to
[YOUR_ID]and recipientEmail to{{ $json.Email }}. - Credential Required: Connect your inboxPlusApi credentials in Compose InboxPlus Email.
- In Assemble HTML Body, set gmailBodyHtml to the provided HTML and keep the personalization token
{{ $('Retrieve Contacts').item.json.Name }}. - Configure Download Email Image with operation set to
downloadand fileId set to[YOUR_ID]. - Credential Required: Connect your googleDriveOAuth2Api credentials in Download Email Image.
- In Send Gmail Notice, set sendTo to
{{ $('Retrieve Contacts').item.json.Email }}, subject to{{ $('Compose InboxPlus Email').item.json.subject }}, and message to{{ $json.gmailBodyHtml }}. - Credential Required: Connect your gmailOAuth2 credentials in Send Gmail Notice.
- In Verify Email Delivered, check if
{{ $json.labelIds }}containsSENT. The true path updates Update Status Sheet, and the false path logs to Log Email Failure.
Step 6: Test and Activate Your Workflow
Validate the end-to-end flow before enabling production execution.
- Click Execute Workflow on Manual Start to run a test batch.
- Confirm that Update Status Sheet updates the contact’s Mail Sent and Message Sent fields to
Sent. - Verify email delivery by checking the Verify Email Delivered condition and WhatsApp delivery through Confirm WhatsApp Sent.
- If failures occur, validate that Log WhatsApp Failure and Log Email Failure are updating the sheet.
- Toggle the workflow to Active once tests pass.
Common Gotchas
- Google Sheets permissions can block updates even when reading works. If statuses aren’t being written back, check the connected Google account access and the specific sheet sharing settings first.
- If you’re using Wait nodes or external rendering, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- Gmail can silently throttle bulk sending behavior. If delivery checks start flipping to failures, review your Gmail sending limits and reduce batch size before you assume the workflow is broken.
Frequently Asked Questions
About 45 minutes if your Sheet and templates are ready.
No. You’ll mostly map fields and adjust a few conditions. The only “technical” part is being careful with your status column values.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in WhatsApp template messaging provider costs (often billed per conversation) and any paid email template tooling you choose.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, and you should. Most teams customize the “pending/sent/failed” values in the Google Sheets checks, then swap the email template in the InboxPlus template node and the WhatsApp template in the WhatsApp send node. You can also add a simple rule like “only send if last_contacted is older than 14 days” to be extra safe. Keep the channels independent so a missing phone number doesn’t block email.
Usually it’s expired Google auth or the wrong Gmail account connected in n8n. Reconnect Gmail credentials, then double-check the “from” mailbox and any required scopes. If you’re sending a lot at once, Gmail limits can also cause temporary failures, so reduce your batch size and retry only the rows marked failed.
A few thousand contacts per run is realistic for most small teams, as long as you keep batch sizes conservative and respect Gmail/WhatsApp rate limits.
Often, yes, because the “no duplicates” part depends on conditional logic, branching, and writing back statuses reliably. n8n makes that kind of control straightforward, and self-hosting avoids per-task pricing when you scale. Zapier or Make can be fine for basic two-step sends, but they get awkward when you need batching plus safe retries. One more thing: WhatsApp template sending is picky about how providers implement it, so you want a workflow you can tweak without rebuilding everything. Talk to an automation expert if you want help picking the simplest route for your volume.
Once your Sheet becomes the system of record, outreach stops feeling fragile. Set it up, run it when you’re ready, and let the workflow handle the repetitive tracking work.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.