Gmail to Google Sheets, unsubscribes logged and removed
You did the hard part: prospecting, personalizing, and sending outreach. Then an “unsubscribe” reply lands… and it gets buried. A week later, the same person gets emailed again because your sheet never got updated.
This Gmail unsubscribe automation hits lead gen specialists hardest, but freelancers and outreach managers feel it too. Not because they don’t care. Because they’re moving fast, juggling lists, and the cleanup work is invisible until it hurts.
This workflow automatically detects unsubscribe intent in Gmail replies, logs the opt-out in Google Sheets, and removes the contact from your main outreach list. You’ll see how it works, what you need, and what to tweak so it matches your campaigns.
How This Automation Works
Here’s the complete workflow you’ll be setting up:
n8n Workflow Template: Gmail to Google Sheets, unsubscribes logged and removed
flowchart LR
subgraph sg0["Watch New replies Flow"]
direction LR
n0@{ icon: "mdi:play-circle", form: "rounded", label: "Watch New replies", pos: "b", h: 48 }
n1@{ icon: "mdi:brain", form: "rounded", label: "OpenAI Chat Model", pos: "b", h: 48 }
n2@{ icon: "mdi:robot", form: "rounded", label: "DETECT INTENT", pos: "b", h: 48 }
n3["<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/code.svg' width='40' height='40' /></div><br/>NORMALIZE AI OUTPUT"]
n4@{ icon: "mdi:database", form: "rounded", label: "CHECK MAIN SHEET", pos: "b", h: 48 }
n5@{ icon: "mdi:database", form: "rounded", label: "Delete rows or columns from ..", pos: "b", h: 48 }
n6@{ icon: "mdi:database", form: "rounded", label: "ADD TO UNSUBSCRIBE", pos: "b", h: 48 }
n7@{ icon: "mdi:message-outline", form: "rounded", label: "ADD UNSUBSCRIBE TO EMAIL", pos: "b", h: 48 }
n8@{ icon: "mdi:database", form: "rounded", label: "Get unsubscribe 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/code.svg' width='40' height='40' /></div><br/>IS EMAIL THERE?"]
n10@{ icon: "mdi:swap-horizontal", form: "rounded", label: "IF IT ALREADY EXISTS, DO NOT..", pos: "b", h: 48 }
n11@{ icon: "mdi:cog", form: "rounded", label: "No Operation, do nothing", pos: "b", h: 48 }
n12@{ icon: "mdi:cog", form: "rounded", label: "No Operation, do nothing1", pos: "b", h: 48 }
n13@{ icon: "mdi:swap-horizontal", form: "rounded", label: "UNSUBSCRIBE?", pos: "b", h: 48 }
n14@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Personal Emails?", pos: "b", h: 48 }
n13 --> n8
n13 --> n11
n2 --> n3
n9 --> n10
n4 --> n5
n14 --> n11
n14 --> n2
n1 -.-> n2
n0 --> n14
n6 --> n4
n3 --> n13
n8 --> n9
n7 --> n12
n10 --> n11
n10 --> n6
n5 --> n7
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 n2 ai
class n1 aiModel
class n10,n13,n14 decision
class n4,n5,n6,n8 database
class n3,n9 code
classDef customIcon fill:none,stroke:none
class n3,n9 customIcon
Why This Matters: Unsubscribes Get Missed (and You Pay for It)
Unsubscribe requests don’t show up as a neat checkbox in your CRM when you’re running list-based outreach. They arrive as messy human replies: “remove me,” “stop emailing,” “not interested,” sometimes with a signature block longer than the message. If you’re managing multiple campaigns or client inboxes, it’s easy to miss one, or forget to update the right sheet. Then you re-email someone who opted out. That can trigger spam complaints, damage deliverability, and create awkward client conversations you honestly don’t need.
It adds up fast. Here’s where it breaks down in real teams.
- Someone replies “unsubscribe,” but the email is read on mobile and never gets logged anywhere.
- You remove them from one list, but they’re still sitting in a second “master” Google Sheet used for follow-ups.
- A VA or teammate updates the sheet later and accidentally deletes the wrong row because multiple contacts share a similar name.
- You keep sending follow-ups because your sending tool only knows what the spreadsheet tells it.
What You’ll Build: Unsubscribe Detection That Updates Sheets Automatically
This workflow watches your Gmail inbox for new replies, then uses OpenAI to interpret the message and decide if it’s an unsubscribe request. If the reply looks like an opt-out, the workflow standardizes that decision (“unsubscribe” vs “keep”) so you don’t rely on fuzzy wording. Next, it checks your Google Sheets opt-out list to avoid duplicates. If the email isn’t already listed, it writes a clean record to an “Unsubscribe Sheet,” removes the matching row from your “Main Outreach Sheet,” and (optionally) applies an “Unsubscribe” label back in Gmail so you can spot these threads instantly. It’s list hygiene that runs in the background, not a new tool your team has to remember.
The workflow starts on a Gmail reply trigger. OpenAI handles intent detection, then a Code step verifies and normalizes the output. Finally, Google Sheets becomes the single source of truth: one sheet to exclude, one sheet to keep clean.
What You’re Building
| What Gets Automated | What You’ll Achieve |
|---|---|
|
|
Expected Results
Say you run outreach for one inbox and you get about 10 replies a day. Manually, reading each reply, deciding if it’s an opt-out, searching the email in Google Sheets, removing the row, and logging it elsewhere can take maybe 3 minutes each, so you’re spending roughly 30 minutes daily. With this workflow: Gmail triggers it, the AI classifies the intent, and Sheets updates automatically in the background. You still skim replies for sales opportunities, but unsubscribe cleanup drops to near zero.
Before You Start
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Gmail for reading replies and applying labels.
- Google Sheets to store your main list and opt-outs.
- OpenAI API key (get it from your OpenAI dashboard under API keys)
Skill level: Intermediate. You’ll connect credentials, map sheet columns, and run a few real-world tests before turning it on.
Want someone to build this for you? Talk to an automation expert (free 15-minute consultation).
Step by Step
A new reply hits your Gmail inbox. The workflow’s Gmail trigger checks for replies on a regular polling schedule (the template uses about every 5 minutes by default).
Personal emails are filtered out early. A quick “personal address” check can stop internal/team emails from being processed, which keeps your opt-out logic focused on outreach replies.
AI classifies the intent, then your logic confirms it. OpenAI reviews the message text for opt-out language, and a Code step standardizes the result to a clean “unsubscribe” or “keep” value so downstream steps don’t get confused.
Google Sheets is updated and Gmail is labeled. If it’s an unsubscribe, the workflow checks your opt-out sheet to avoid duplicates, appends a record when needed, removes the contact from your main outreach sheet, and applies an “Unsubscribe” Gmail label (optional).
You can easily modify the polling frequency to match your sending volume, or swap the sheet structure to fit your existing columns. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Gmail Trigger
Set up the trigger that watches for incoming email replies and starts the unsubscribe flow.
- Add and open Email Reply Watcher.
- Set Include Spam & Trash to
true(already enabled in filters). - Set Poll Times to every
5minutes. - Credential Required: Connect your Gmail credentials.
Step 2: Connect Google Sheets
Configure the sheets that store your opt-out list and main contact records.
- Open Lookup Main Sheet and set Document ID and Sheet Name to your source spreadsheet IDs (currently
[YOUR_ID]). - In Lookup Main Sheet, set the filter to EMAIL with lookup value
{{ $('Email Reply Watcher').item.json.From.match(/<([^>]+)>/)?.[1]?.trim().toLowerCase() || $('Email Reply Watcher').item.json.From.trim().toLowerCase() }}. - Credential Required: Connect your
googleSheetsOAuth2Apicredentials in Lookup Main Sheet. - Open Retrieve Opt-out Sheet, Append Opt-out Record, and Remove Sheet Row and replace Document ID and Sheet Name values (currently
[YOUR_ID]). - Credential Required: Connect Google Sheets credentials for Retrieve Opt-out Sheet, Append Opt-out Record, and Remove Sheet Row (they are set to
serviceAccountauthentication but credentials are not configured).
EMAIL, DATE, WHERE, and MESSAGE for inserts to succeed.Step 3: Set Up AI Intent Detection
Use the AI chain to classify whether the reply is an unsubscribe request.
- Open OpenAI Chat Engine and set Model to
gpt-4o-mini, Max Tokens to20, and Temperature to0.1. - Credential Required: Connect your OpenAI credentials in OpenAI Chat Engine.
- Open Intent Detection Chain and set Prompt to
=You are a message intent detector for outreach emails. Your job is to determine if this message means the sender wants to stop receiving emails or unsubscribe. Reply strictly with one word: - "unsubscribe" → if they indicate they don't want further contact (even indirectly) - "keep" → if they are interested, neutral, or unrelated. Message: {{ $json.snippet }}. - Verify OpenAI Chat Engine is connected as the language model for Intent Detection Chain; credentials are added to OpenAI Chat Engine, not the chain.
- Open Standardize AI Result and keep the JavaScript as-is to normalize the AI response and set
isUnsubscribe.
unsubscribe or keep to avoid breaking Unsubscribe Decision.Step 4: Configure the Unsubscribe Decision Path
Filter personal addresses, detect unsubscribe intent, and prevent duplicate entries.
- Open Personal Address Filter and set the condition to check if From contains
[YOUR_EMAIL]using left value{{ $json.From }}. Replace[YOUR_EMAIL]with your actual sender address or domain. - Open Unsubscribe Decision and ensure the condition compares
{{ $json.text }}tounsubscribe. - Open Retrieve Opt-out Sheet and confirm it reads from the same opt-out sheet you append to.
- In Check Email Presence, keep the JavaScript to skip emails already in the opt-out sheet and output
{ Email: currentEmail }when not present. - Open Existing Entry Gate and confirm the condition checks
{{ $json.Email }}equals{{ $('Retrieve Opt-out Sheet').item.json.EMAIL }}.
Email instead of EMAIL), update both Retrieve Opt-out Sheet and Existing Entry Gate references.Step 5: Configure the Output Actions
Append the opt-out record, remove the contact from the main sheet, and label the original Gmail thread.
- Open Append Opt-out Record and map the columns as follows: DATE to
{{ new Date().toISOString().split('T')[0] }}, EMAIL to{{ $('Check Email Presence').first().json.Email }}, WHERE toUNSUBSCRIBE MESSAGE REPLY, and MESSAGE to{{ $('Email Reply Watcher').item.json.snippet }}. - Open Remove Sheet Row and keep Operation set to
deleteand Start Index set to{{ $json.row_number }}. - Open Apply Gmail Opt-out Label and set Message ID to
{{ $('Email Reply Watcher').item.json.id }}with OperationaddLabelsand label ID set to your opt-out label (replace[YOUR_ID]). - Credential Required: Connect your Gmail credentials in Apply Gmail Opt-out Label.
Step 6: Test and Activate Your Workflow
Validate the end-to-end flow, then enable it for production.
- Click Execute Workflow and send a test reply email that includes an unsubscribe intent (e.g., “please unsubscribe”).
- Verify the path runs in this order: Email Reply Watcher → Personal Address Filter → Intent Detection Chain → Standardize AI Result → Unsubscribe Decision → Retrieve Opt-out Sheet → Check Email Presence → Existing Entry Gate → Append Opt-out Record → Lookup Main Sheet → Remove Sheet Row → Apply Gmail Opt-out Label.
- Confirm a new opt-out row is appended, the contact row is removed from your main sheet, and the Gmail label is applied.
- Turn the workflow Active to enable continuous polling and processing.
Troubleshooting Tips
- Gmail credentials can expire or need specific permissions. If things break, check the n8n Credentials Manager and confirm the Gmail scope still allows reading replies and applying labels.
- If you’re using Wait nodes or external rendering, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- Default prompts in AI nodes are generic. Add your brand voice early or you’ll be editing outputs forever.
Quick Answers
About 30 minutes if your Gmail, Sheets, and OpenAI accounts are ready.
No. You’ll mostly connect credentials and match your Google Sheets columns. The included Code step is prebuilt and only needs edits if you want custom rules.
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 OpenAI API costs, which are usually a few cents per day for low reply volume.
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 probably should. You can adjust the “Email Reply Watcher” polling interval, swap your sheet IDs in the Google Sheets steps, and customize the “Standardize AI Result” logic so it recognizes extra phrases your audience uses. Many teams also disable “Apply Gmail Opt-out Label” if they prefer to keep inbox labeling manual.
Most of the time it’s expired OAuth access or the wrong Gmail account connected in n8n. Reconnect Gmail in the Credentials Manager, then re-run a test execution using a real reply email. Also confirm the workflow has permission to apply labels if you kept the Gmail labeling step enabled.
For most small teams, hundreds of replies per day is fine.
Often, yes, because this workflow benefits from multi-step logic: filter personal replies, run AI intent detection, standardize results, check for duplicates, then update two sheets and Gmail. n8n handles branching cleanly, and self-hosting can be a big deal if you’re processing lots of inbox traffic without wanting per-task fees. Zapier or Make can still work, but AI classification plus deduping plus sheet row deletion usually turns into a fragile chain. If you just need “label email when it contains the word unsubscribe,” those tools are fine. If you want list hygiene you can trust, n8n is a safer bet. Talk to an automation expert if you want help choosing.
Once this is running, opt-outs stop being a stressful “did we remember?” task. Your sheets stay clean, your inbox stays readable, and your outreach stays safer for the long haul.
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.