Google Sheets + SendGrid: leads tracked end to end
Your lead list is never really “done.” You scrape a batch, paste it into a sheet, send a few emails, then lose track of who opened, who bounced, and who actually replied.
Sales managers feel it when follow-ups slip. A marketing ops person feels it when reporting turns into detective work. And if you run an agency, Sheets SendGrid leads automation is the difference between “we emailed them” and “we can prove what happened.”
This workflow ties the whole loop together: scrape Google Maps leads, store them in Google Sheets, send tracked outreach in SendGrid, then capture replies from Gmail and route them with AI. You’ll see how it works, what you need, and what results to expect.
How This Automation Works
Here’s the complete workflow you’ll be setting up:
n8n Workflow Template: Google Sheets + SendGrid: leads tracked end to end
flowchart LR
subgraph sg0["Schedule Flow"]
direction LR
n64@{ icon: "mdi:database", form: "rounded", label: "Update DB1", pos: "b", h: 48 }
n65@{ icon: "mdi:database", form: "rounded", label: "Update DB2", pos: "b", h: 48 }
n66@{ icon: "mdi:message-outline", form: "rounded", label: "Send a message", pos: "b", h: 48 }
n67@{ icon: "mdi:swap-vertical", form: "rounded", label: "Forward Emails (Sales Dept)", pos: "b", h: 48 }
n68["<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/>Email Structure"]
n69@{ icon: "mdi:database", form: "rounded", label: "Update DB3", pos: "b", h: 48 }
n70@{ icon: "mdi:message-outline", form: "rounded", label: "Label Interested", pos: "b", h: 48 }
n71@{ icon: "mdi:message-outline", form: "rounded", label: "Label Not Interested", pos: "b", h: 48 }
n72@{ icon: "mdi:message-outline", form: "rounded", label: "Label Misc", pos: "b", h: 48 }
n73@{ icon: "mdi:swap-vertical", form: "rounded", label: "Leads Converted", pos: "b", h: 48 }
n74@{ icon: "mdi:robot", form: "rounded", label: "Create email labels", pos: "b", h: 48 }
n75@{ icon: "mdi:database", form: "rounded", label: "Get Route_Message_Id", pos: "b", h: 48 }
n76@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Only Emails with ID", pos: "b", h: 48 }
n77@{ icon: "mdi:brain", form: "rounded", label: "OpenAI Chat Model", pos: "b", h: 48 }
n78@{ icon: "mdi:play-circle", form: "rounded", label: "Schedule Trigger1", pos: "b", h: 48 }
n79@{ icon: "mdi:database", form: "rounded", label: "Update DB4", pos: "b", h: 48 }
n80["<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/>Extract ID1"]
n81@{ icon: "mdi:swap-vertical", form: "rounded", label: "Edit Fields1", pos: "b", h: 48 }
n82@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Email reply only1", pos: "b", h: 48 }
n83["<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/merge.svg' width='40' height='40' /></div><br/>Extract unprocessed emails1"]
n84@{ icon: "mdi:cog", form: "rounded", label: "No Operation, do nothing2", pos: "b", h: 48 }
n85@{ icon: "mdi:message-outline", form: "rounded", label: "Get many messages (20)1", pos: "b", h: 48 }
n72 --> n65
n69 --> n73
n80 --> n76
n81 --> n69
n66 --> n67
n68 --> n66
n73 --> n81
n70 --> n79
n82 --> n83
n77 -.-> n74
n78 --> n85
n78 --> n75
n74 --> n70
n74 --> n67
n74 --> n73
n74 --> n71
n74 --> n72
n76 --> n74
n76 --> n84
n75 --> n83
n71 --> n64
n85 --> n82
n83 --> n80
n67 --> n68
end
subgraph sg1["Schedule Flow"]
direction LR
n86@{ icon: "mdi:database", form: "rounded", label: "Grab Lead from DB", pos: "b", h: 48 }
n87["<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/>Pick a random template1"]
n88@{ icon: "mdi:cog", form: "rounded", label: "No Operation, do nothing3", pos: "b", h: 48 }
n89@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Email Opened Or Clicked", pos: "b", h: 48 }
n90@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Email Delivered But No Replied", pos: "b", h: 48 }
n91@{ icon: "mdi:swap-horizontal", form: "rounded", label: "5 Days & 10 Days Route", pos: "b", h: 48 }
n92@{ icon: "mdi:swap-vertical", form: "rounded", label: "Personalize Email", pos: "b", h: 48 }
n93@{ icon: "mdi:swap-vertical", form: "rounded", label: "Personalize Email1", pos: "b", h: 48 }
n94["<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/sendGrid.svg' width='40' height='40' /></div><br/>Send an email1"]
n95@{ icon: "mdi:database", form: "rounded", label: "Get Follow-Up Email Template..", pos: "b", h: 48 }
n96@{ icon: "mdi:database", form: "rounded", label: "Get 2nd Follow-Up Email Temp..", pos: "b", h: 48 }
n97@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items4", pos: "b", h: 48 }
n98["<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/merge.svg' width='40' height='40' /></div><br/>Merge4"]
n99@{ icon: "mdi:cog", form: "rounded", label: "Wait4", pos: "b", h: 48 }
n100["<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/>Pick a random template2"]
n101@{ icon: "mdi:play-circle", form: "rounded", label: "Schedule Trigger2", pos: "b", h: 48 }
n102@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items5", pos: "b", h: 48 }
n103["<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/merge.svg' width='40' height='40' /></div><br/>Merge5"]
n104@{ icon: "mdi:cog", form: "rounded", label: "Wait5", pos: "b", h: 48 }
n105@{ icon: "mdi:cog", form: "rounded", label: "No Operation, do nothing4", pos: "b", h: 48 }
n106["<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/sendGrid.svg' width='40' height='40' /></div><br/>Send an email2"]
n99 --> n97
n104 --> n102
n98 --> n92
n103 --> n93
n94 --> n104
n106 --> n99
n97 --> n95
n97 --> n98
n102 --> n96
n102 --> n103
n86 --> n90
n92 --> n106
n101 --> n86
n93 --> n94
n91 --> n97
n91 --> n102
n89 --> n91
n89 --> n88
n87 --> n103
n100 --> n98
n90 --> n89
n90 --> n105
n95 --> n100
n96 --> n87
end
subgraph sg2["On form submission Flow"]
direction LR
n0@{ icon: "mdi:cog", form: "rounded", label: "Limit", pos: "b", h: 48 }
n1["<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/form.svg' width='40' height='40' /></div><br/>On form submission"]
n2@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Run an Actor", pos: "b", h: 48 }
n3@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Get dataset items", pos: "b", h: 48 }
n4@{ icon: "mdi:swap-vertical", form: "rounded", label: "Grab Desired Fields", pos: "b", h: 48 }
n5@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items", pos: "b", h: 48 }
n6["<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/markdown.dark.svg' width='40' height='40' /></div><br/>Markdown"]
n7@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items1", pos: "b", h: 48 }
n8@{ icon: "mdi:cog", form: "rounded", label: "Wait1", 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/merge.svg' width='40' height='40' /></div><br/>Merge"]
n10@{ icon: "mdi:swap-vertical", form: "rounded", label: "Grab Desired Fields1", pos: "b", h: 48 }
n11["<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/merge.svg' width='40' height='40' /></div><br/>Merge1"]
n12@{ icon: "mdi:swap-vertical", form: "rounded", label: "Parse url/website", pos: "b", h: 48 }
n13["<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/>Remove Query Parameters & Fr.."]
n14@{ icon: "mdi:swap-vertical", form: "rounded", label: "User-Agents", pos: "b", h: 48 }
n15["<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/httprequest.dark.svg' width='40' height='40' /></div><br/>Website Scraping"]
n16@{ icon: "mdi:cog", form: "rounded", label: "Random Wait", pos: "b", h: 48 }
n17["<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/>Extract Email Address"]
n18@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Filter Leads with Email only", pos: "b", h: 48 }
n19@{ icon: "mdi:database", form: "rounded", label: "Append row in sheet", pos: "b", h: 48 }
n0 --> n5
n9 --> n5
n8 --> n11
n11 --> n7
n6 --> n16
n16 --> n9
n14 --> n15
n2 --> n3
n5 --> n7
n5 --> n12
n5 --> n9
n7 --> n18
n7 --> n17
n7 --> n11
n15 --> n6
n3 --> n4
n12 --> n13
n1 --> n2
n4 --> n0
n10 --> n19
n17 --> n8
n18 --> n10
n13 --> n14
end
subgraph sg3["Flow 4"]
direction LR
n32["<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/webhook.dark.svg' width='40' height='40' /></div><br/>Webhook"]
n33@{ icon: "mdi:swap-vertical", form: "rounded", label: "Split Out", pos: "b", h: 48 }
n34@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Switch", pos: "b", h: 48 }
n35@{ icon: "mdi:database", form: "rounded", label: "Original DB Update", pos: "b", h: 48 }
n36@{ icon: "mdi:database", form: "rounded", label: "Follow-Up 1 DB Update", pos: "b", h: 48 }
n37@{ icon: "mdi:database", form: "rounded", label: "Follow-Up 2 DB Update", pos: "b", h: 48 }
n38["<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/>Classify Events"]
n39@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Route Email Stages", pos: "b", h: 48 }
n40@{ icon: "mdi:database", form: "rounded", label: "Get row(s) in sheet", pos: "b", h: 48 }
n41["<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/>Classify Engagements"]
n42@{ icon: "mdi:swap-vertical", form: "rounded", label: "Matching field", pos: "b", h: 48 }
n43["<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/>Prioritize DELIVERED first"]
n44@{ icon: "mdi:database", form: "rounded", label: "Update DB", pos: "b", h: 48 }
n45@{ icon: "mdi:cog", form: "rounded", label: "Wait2", pos: "b", h: 48 }
n46@{ icon: "mdi:cog", form: "rounded", label: "No Operation, do nothing1", pos: "b", h: 48 }
n47@{ icon: "mdi:cog", form: "rounded", label: "Wait3", pos: "b", h: 48 }
n48["<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/merge.svg' width='40' height='40' /></div><br/>Merge3"]
n45 --> n39
n47 --> n44
n48 --> n41
n34 --> n43
n34 --> n40
n34 --> n42
n34 --> n46
n32 --> n33
n33 --> n38
n42 --> n48
n38 --> n34
n39 --> n35
n39 --> n36
n39 --> n37
n40 --> n48
n41 --> n47
n43 --> n45
end
subgraph sg4["Schedule Flow"]
direction LR
n49@{ icon: "mdi:swap-vertical", form: "rounded", label: "Edit Fields", pos: "b", h: 48 }
n50["<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/>Extract ID"]
n51@{ icon: "mdi:play-circle", form: "rounded", label: "Schedule Trigger", pos: "b", h: 48 }
n52@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Parse items with Extract ID ..", pos: "b", h: 48 }
n53@{ icon: "mdi:database", form: "rounded", label: "Original Email", pos: "b", h: 48 }
n54@{ icon: "mdi:database", form: "rounded", label: "1st Follow-Up Email", pos: "b", h: 48 }
n55@{ icon: "mdi:database", form: "rounded", label: "2nd Follow-Up Email", pos: "b", h: 48 }
n56@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Email reply only", pos: "b", h: 48 }
n57@{ icon: "mdi:database", form: "rounded", label: "Get Reply_Message_Id", pos: "b", h: 48 }
n58["<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/merge.svg' width='40' height='40' /></div><br/>Extract unprocessed emails"]
n59@{ icon: "mdi:database", form: "rounded", label: "Update Email Workflow DB", pos: "b", h: 48 }
n60@{ icon: "mdi:database", form: "rounded", label: "Update Processed Gmail IDs DB", pos: "b", h: 48 }
n61@{ icon: "mdi:message-outline", form: "rounded", label: "Get many messages (20)", pos: "b", h: 48 }
n62@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items3", pos: "b", h: 48 }
n63@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Switch1", pos: "b", h: 48 }
n63 --> n53
n63 --> n54
n63 --> n55
n50 --> n49
n49 --> n62
n56 --> n58
n62 --> n52
n62 --> n50
n51 --> n61
n51 --> n57
n57 --> n58
n61 --> n56
n58 --> n62
n52 --> n59
n52 --> n63
n52 --> n60
end
subgraph sg5["When clicking âExecute workflowâ Flow"]
direction LR
n20@{ icon: "mdi:play-circle", form: "rounded", label: "When clicking âExecute wor..", pos: "b", h: 48 }
n21@{ icon: "mdi:cog", form: "rounded", label: "Wait", pos: "b", h: 48 }
n22@{ icon: "mdi:cog", form: "rounded", label: "No Operation, do nothing", pos: "b", h: 48 }
n23@{ icon: "mdi:database", form: "rounded", label: "Parse Email Template from DB", pos: "b", h: 48 }
n24["<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/>Pick a random template"]
n25["<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/sendGrid.svg' width='40' height='40' /></div><br/>Send an email"]
n26@{ icon: "mdi:swap-vertical", form: "rounded", label: "Fix Variables", pos: "b", h: 48 }
n27@{ icon: "mdi:database", form: "rounded", label: "Get Leads", pos: "b", h: 48 }
n28@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Email Not Sent (Not Delivered)", pos: "b", h: 48 }
n29@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items2", pos: "b", h: 48 }
n30["<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/merge.svg' width='40' height='40' /></div><br/>Merge2"]
n31@{ icon: "mdi:cog", form: "rounded", label: "Limit1", pos: "b", h: 48 }
n21 --> n29
n31 --> n29
n30 --> n26
n27 --> n28
n26 --> n25
n25 --> n21
n29 --> n23
n29 --> n30
n24 --> n30
n23 --> n24
n28 --> n31
n28 --> n22
n20 --> n27
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 n78,n101,n1,n51,n20 trigger
class n74 ai
class n77 aiModel
class n76,n82,n89,n90,n91,n2,n3,n18,n34,n39,n52,n56,n63,n28 decision
class n64,n65,n69,n75,n79,n86,n95,n96,n19,n35,n36,n37,n40,n44,n53,n54,n55,n57,n59,n60,n23,n27 database
class n15,n32 api
class n68,n80,n87,n100,n13,n17,n38,n41,n43,n50,n24 code
classDef customIcon fill:none,stroke:none
class n68,n80,n83,n87,n94,n98,n100,n103,n106,n1,n6,n9,n11,n13,n15,n17,n32,n38,n41,n43,n48,n50,n58,n24,n25,n30 customIcon
Why This Matters: Lead tracking breaks after the send
Most cold outreach “systems” fall apart right after the email goes out. The sheet has a row that says “sent,” but nobody knows if it delivered, bounced, or got opened three times. Replies land in a shared inbox, then get forwarded to someone, then disappear into Slack with no link back to the lead record. A week later, you’re chasing follow-ups off memory and half-complete notes. Honestly, that’s not a process. It’s a scramble that gets worse as volume grows.
It adds up fast. Here’s where it breaks down in real life:
- You send emails in batches, but delivery and bounce status never makes it back into Google Sheets.
- Replies are handled manually, so “Interested” leads sit in an inbox until someone notices.
- Follow-ups happen late (or not at all) because nobody wants to filter, sort, and check dates every day.
- Your reporting ends up fuzzy, because opens/clicks live in SendGrid logs while lead data lives somewhere else.
What You’ll Build: A lead lifecycle loop from scrape to reply
This workflow runs your outreach like a connected system instead of six separate tasks. It starts with a simple form submission (keyword, location, lead limit) that triggers a Google Maps scrape through Apify. Each business gets cleaned up, checked for a website, and scanned for a public email address. Qualified leads are added into your Google Sheets CRM, which becomes the single source of truth for outreach. From there, the workflow pulls a rotating email template, personalizes it with lead data, embeds a unique tracking ID, and sends through SendGrid. As SendGrid events come in via webhooks (delivered, opened, clicked, bounced, unsubscribed), the workflow updates the exact lead row with prioritized status and timestamps. Finally, Gmail reply polling catches responses, avoids duplicates, uses OpenAI to classify intent, and routes “Interested” replies to your team while still updating Sheets.
The loop is simple to think about. New leads enter through Google Maps scraping, outreach goes out through SendGrid with tracking baked in, and every signal (events and replies) pushes cleanly back into your CRM so follow-ups happen on time.
What You’re Building
| What Gets Automated | What You’ll Achieve |
|---|---|
|
|
Expected Results
Say you scrape 50 local leads and run outreach twice a week. Manually, it’s easy to burn about 2 hours scraping/exporting, another hour cleaning websites/emails, and at least an hour over the week checking SendGrid stats, logging replies, and setting follow-ups. With this workflow, you submit the form in a minute, let scraping and extraction run in the background (often 20–40 minutes depending on sites), and you spend your time only on real responses. Most teams get about 3 hours back per campaign cycle, plus fewer “we forgot to follow up” moments.
Before You Start
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets for your CRM and templates.
- SendGrid to send tracked outreach and receive webhook events.
- Apify API key (get it from your Apify account settings).
- Gmail API credentials (create in Google Cloud Console for reply polling).
- OpenAI API key (get it from the OpenAI dashboard) for reply classification.
Skill level: Intermediate. You won’t code from scratch, but you should be comfortable connecting accounts, mapping fields, and testing webhooks.
Want someone to build this for you? Talk to an automation expert (free 15-minute consultation).
Step by Step
A form submission kicks things off. You enter a business keyword, a location, and how many leads you want. That one input triggers the Apify Google Maps scrape.
Lead data gets cleaned, filtered, and enriched. The workflow removes entries without websites, normalizes URLs, rotates user agents, and fetches each site’s HTML to look for public email addresses. It also uses delays between requests to avoid triggering blocks.
Outreach sends with tracking built in. n8n pulls your template library from Google Sheets, picks a random option, injects lead variables, and sends through SendGrid with a unique embedded ID so future events and replies match the right record.
Status, engagement, and replies flow back to your CRM. SendGrid webhooks update “delivered/opened/clicked/bounced/unsubscribed” fields with priority logic, while Gmail polling captures replies, avoids duplicates, and uses OpenAI to label intent and forward the important ones to your team (Slack/Telegram routing is supported in the workflow).
You can easily modify follow-up timing and routing destinations based on your needs. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Form Submission Trigger
This workflow starts when a form is submitted and also includes scheduled and webhook-based triggers for ongoing email processing.
- Add and open Form Submission Trigger to capture inbound leads from your form.
- Optionally enable Manual Execution Start for ad-hoc runs of the lead list intake.
- Set up Scheduled Trigger A and Scheduled Trigger B to poll Gmail replies on a schedule, and Scheduled Trigger C to check delivery/no-reply follow-ups.
- Enable Incoming Webhook if you plan to push engagement events into this workflow.
Scheduled Trigger A outputs to both Fetch Recent Gmail and Get Reply Message ID in parallel. Scheduled Trigger B outputs to both Fetch Recent Gmail B and Get Route Message ID in parallel.
Step 2: Connect Google Sheets
Multiple Google Sheets nodes store lead lists, templates, and status updates. Connect credentials once and apply to all Sheets nodes.
- Open any Google Sheets node (for example, Retrieve Lead List or Append Sheet Row).
- Credential Required: Connect your Google Sheets credentials.
- Repeat the credential selection for all Google Sheets nodes that manage templates and statuses (22+ nodes, including Load Email Template DB, Update Original DB, Update DB Status 1, Update DB Status 2, Update DB Status 3, and Update DB Status 4).
- Verify sheet IDs, ranges, and write operations in nodes like Append Sheet Row and Modify Processed IDs DB.
⚠️ Common Pitfall: Missing Google Sheets credentials will cause all read/write steps to fail silently, especially in branches like Route Email Phases and Filter Items w ID.
Step 3: Set Up Lead Enrichment Processing
This phase pulls leads from Apify, cleans URLs, and enriches data via web page requests.
- Configure Execute Actor Task to run your Apify actor and pass lead input data from Form Submission Trigger.
- Credential Required: Connect your Apify credentials in Execute Actor Task and Fetch Dataset Items.
- Use Select Needed Fields and Parse Website URL to normalize lead records before scraping.
- Clean and prep URLs via Strip URL Extras and then set headers in Assign User Agents.
- Configure Web Page Request to fetch the lead website content, then format it using Render Markdown.
Tip: Render Markdown and Web Page Request are configured to continue on error—ensure your downstream steps can handle empty content.
Step 4: Configure Outreach Email Dispatch
This step batches leads, selects templates, personalizes content, and sends emails via SendGrid.
- Control volume with Cap Records and Batch Iterate Items before personalization.
- Normalize variables in Normalize Variables after templates are merged in Combine Streams B.
- Load templates from Load Email Template DB and choose one using Choose Random Template.
- Send outreach via Dispatch Email, then queue follow-up timing in Delay Step.
- Credential Required: Connect your SendGrid credentials in Dispatch Email, Dispatch Email B, and Dispatch Email C.
Dispatch Email outputs to Delay Step, which then flows into Batch Iterate Items B for continuous batching.
Step 5: Set Up Create Email Labels AI Classification
Incoming replies are labeled and routed using the text classifier node connected to OpenAI.
- Configure Create Email Labels to classify replies into outcomes like Interested, Not Interested, or Misc.
- Attach OpenAI Chat Engine as the language model for Create Email Labels.
- Credential Required: Connect your OpenAI credentials in OpenAI Chat Engine (credentials are added to the parent AI node, not Create Email Labels).
Create Email Labels outputs to Apply Label Interested, Forward Emails to Sales, and Process Converted Leads in parallel.
Step 6: Configure Gmail Intake and Routing
Reply processing uses scheduled Gmail pulls, filtering, and routing to match replies with workflows.
- Credential Required: Connect your Gmail credentials in Fetch Recent Gmail, Fetch Recent Gmail B, Send Gmail Message, Apply Label Interested, Apply Label Not Interested, and Apply Label Misc.
- Filter replies with Filter Reply Emails and Filter Reply Emails B, then merge in Merge Unprocessed Emails and Merge Unprocessed Emails B.
- Extract identifiers with Extract Message ID and Extract Route ID before evaluating Only Emails With ID.
- Route by template using Route by Template, which sends to Load Original Email, Load Follow-Up Email 1, or Load Follow-Up Email 2.
Filter Items w ID outputs to Modify Email Workflow DB, Route by Template, and Modify Processed IDs DB in parallel.
Step 7: Configure Follow-Up Logic and Engagement Updates
This branch handles engagement categorization, follow-up timing, and database updates for each phase.
- Process engagement with Classify Event Types and route logic using Route by Condition.
- Combine engagement data via Combine Streams C then score via Categorize Engagements.
- Use Check Delivered No Reply and Check Opened Or Clicked to decide follow-up timing in Route 5 or 10 Days.
- Load follow-up templates via Load Follow-Up Template DB and Load Second Follow-Up DB, then personalize in Personalize Email A and Personalize Email B.
- Send follow-ups through Dispatch Email C and Dispatch Email B, followed by Delay Step D and Delay Step E.
Tip: The wait nodes (Delay Step A, Delay Step B, Delay Step C, Delay Step D, Delay Step E, and Randomized Pause) help pace outreach—adjust durations carefully to stay compliant.
Step 8: Review No-Op Placeholders and Debug Paths
No-op nodes exist to hold future logic or for debugging; verify whether you need to replace them with real actions.
- Inspect No-Op Placeholder, No-Op Placeholder A, No-Op Placeholder B, No-Op Placeholder C, and No-Op Placeholder D.
- Decide whether these nodes should log, notify, or branch to new actions in your production workflow.
Step 9: Test and Activate Your Workflow
Validate each trigger path, then activate the workflow for production.
- Use Manual Execution Start to run a controlled test of Retrieve Lead List and confirm outbound emails are sent.
- Trigger Form Submission Trigger with sample data and verify the Apify, enrichment, and sheet-update branches complete.
- Check the Gmail branches by running Scheduled Trigger A and Scheduled Trigger B and confirm labels and routing are applied correctly.
- Successful execution should result in template selection, SendGrid delivery, and updated statuses in Google Sheets.
- When everything checks out, toggle the workflow to Active to run in production.
Troubleshooting Tips
- SendGrid credentials and webhook signing settings can trip you up. If events aren’t updating Google Sheets, check SendGrid’s Event Webhook page and confirm the n8n webhook URL is correct and enabled.
- If you’re using Wait nodes or external scraping, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- Default prompts in the AI classification nodes are generic. Add your definitions of “Interested” and “Not Interested” early or you’ll be relabeling replies manually.
Quick Answers
Plan for about 60–90 minutes if you already have your keys and sheets ready.
No. You will mostly connect accounts and adjust a few field mappings.
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 (often a few dollars a month for typical reply classification) and Apify usage depending on scrape 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 swap the Apify Google Maps scraper for a different Apify actor in the “Execute Actor Task” step, or replace the Google Sheets CRM with Airtable while keeping the SendGrid webhooks intact. Common customizations include changing the follow-up schedule (5/10 days to your cadence), rewriting the template library in the Templates sheet, and routing “Interested” replies to a different place (Slack, Telegram, or a sales inbox).
Usually it’s an API key problem or missing permissions on the key. Update the SendGrid credential in n8n, then verify Event Webhook is enabled inside SendGrid and sending the event types you expect (delivered, open, click, bounce). If you changed the webhook URL after testing, SendGrid may still be posting to the old one. Also check rate limiting if you’re sending large batches at once.
If you self-host, there’s no execution limit (it mostly depends on your server, SendGrid limits, and how aggressive your scraping is). On n8n Cloud, your plan sets monthly executions, so higher-volume outreach usually means upgrading. Practically, most teams run this in daily batches of 20–200 leads to keep deliverability healthy and keep website scraping from getting blocked.
Often, yes. This workflow mixes scraping, batching, waits, prioritization logic for event statuses, and AI classification, which is where Zapier scenarios get expensive or awkward fast. n8n also gives you self-hosting, so you can run high-frequency webhook updates without counting every step like it’s a precious resource. If you only need “new row in Sheets → send email,” Zapier or Make is fine. Once you care about reply routing and lifecycle tracking, n8n is the more practical tool. If you want a second opinion before you commit, Talk to an automation expert.
Once this is running, your Google Sheet stops being a dumping ground and turns into a live pipeline you can actually operate. The workflow handles the repetitive updates, so you can focus on the conversations that turn into revenue.
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.