This workflow corresponds to n8n.io template #7752 — we link there as the canonical source.
This workflow follows the Gmail → Gmail Trigger recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "R0TtQlb7ZSQkTlC7",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Reverse Outreach",
"tags": [],
"nodes": [
{
"id": "8bb1f909-10a4-44d2-a79c-662c37ad65d1",
"name": "Manual Trigger (Historical Run)",
"type": "n8n-nodes-base.manualTrigger",
"position": [
800,
208
],
"parameters": {},
"typeVersion": 1
},
{
"id": "f47e6b4e-0335-4375-ac24-2f19c99edcf8",
"name": "Gmail Trigger (Real-time)",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
1088,
608
],
"parameters": {
"filters": {},
"pollTimes": {
"item": [
{
"hour": 8
}
]
}
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "87f1bb54-8165-4bec-8495-0e385cc0f097",
"name": "Get many messages",
"type": "n8n-nodes-base.gmail",
"position": [
1088,
208
],
"parameters": {
"limit": 500,
"filters": {},
"operation": "getAll"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "f792b037-62f8-4fac-9ea8-b30197cbc62c",
"name": "Code",
"type": "n8n-nodes-base.code",
"position": [
1456,
208
],
"parameters": {
"jsCode": "return items.map(item => {\n let fromField = item.json.From || \"\";\n fromField = fromField.trim();\n\n // Extract display name and email using regex\n const match = fromField.match(/(.*)<(.+@.+)>/);\n let companyName = \"\";\n let email = \"\";\n\n if (match) {\n companyName = match[1].trim() || \"\";\n email = match[2].trim().toLowerCase();\n } else {\n // If no angle brackets, assume just the email\n email = fromField.toLowerCase();\n companyName = email.split(\"@\")[0];\n }\n\n if (!email.includes(\"@\")) return null;\n\n const [localPart, domain] = email.split(\"@\");\n\n // Filter system/service emails\n const blockedLocalParts = [\n 'no-reply', 'noreply', 'donotreply', 'do-not-reply', 'system', 'mailer-daemon', 'automated', 'service', 'bot', 'notifications', 'updates'\n ];\n if (blockedLocalParts.some(blocked => localPart.includes(blocked))) return null;\n\n // Filter generic company inboxes\n const genericLocalParts = [\n 'info', 'support', 'admin', 'help', 'contact', 'sales', 'marketing', 'enquiries', 'hr', 'jobs', 'careers', 'team', 'office'\n ];\n if (genericLocalParts.includes(localPart)) return null;\n\n // Format date naturally\n const timestamp = parseInt(item.json.internalDate, 10);\n const date = new Date(timestamp);\n const formattedDate = date.toLocaleString('en-US', {\n weekday: 'short',\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n hour12: true\n });\n\n return {\n json: {\n company_name: companyName || domain,\n email: email,\n domain: domain,\n subject: item.json.Subject,\n date_received: formattedDate\n }\n };\n}).filter(Boolean);"
},
"typeVersion": 2,
"alwaysOutputData": false
},
{
"id": "76b87209-dc5b-4d9c-823b-4ad27f6e24d1",
"name": "Send a message",
"type": "n8n-nodes-base.slack",
"position": [
2784,
208
],
"parameters": {
"text": "=New Lead added {{$json[\"company_name\"]}} | {{$json[\"email\"]}}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "REPLACE_WITH_SLACK_CHANNEL_ID"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "330863ce-e243-4ddc-b4bb-6f86bd73bfb3",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
2720,
80
],
"parameters": {
"color": 7,
"width": 224,
"height": 320,
"content": "## Notify in Slack channel \n"
},
"typeVersion": 1
},
{
"id": "b2c1be84-f8d9-44ba-8f5c-90e18e6bab52",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
2432,
80
],
"parameters": {
"color": 7,
"width": 224,
"height": 320,
"content": "## Store leads Into Google Sheet\n"
},
"typeVersion": 1
},
{
"id": "c62db656-466e-403d-89e9-147625c4be9f",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1392,
80
],
"parameters": {
"color": 7,
"width": 224,
"height": 304,
"content": "## Filter out the service emails\n(i.e. info@company.com)"
},
"typeVersion": 1
},
{
"id": "eb0ee374-7b56-4d70-ad61-e8d710f831e9",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
992,
448
],
"parameters": {
"color": 7,
"width": 272,
"height": 320,
"content": "## Run email fetching periodically\ndefault is set to daily"
},
"typeVersion": 1
},
{
"id": "5e895d51-5a9b-4149-bf4e-4139d3694a06",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
992,
64
],
"parameters": {
"color": 7,
"width": 272,
"height": 320,
"content": "## Run email fetching for the existing emails"
},
"typeVersion": 1
},
{
"id": "dc5b438f-ee73-4c43-b250-c26c6d9dde66",
"name": "Code1",
"type": "n8n-nodes-base.code",
"position": [
2160,
208
],
"parameters": {
"jsCode": "// This script filters the input items to remove duplicate emails.\n// It keeps the first occurrence of each unique email and discards subsequent ones.\n\nconst uniqueItems = [];\nconst seenEmails = new Set();\n\nfor (const item of items) {\n const email = item.json.email;\n \n // Check if we have already seen this email\n if (!seenEmails.has(email)) {\n // If not, add the email to our set and keep the item\n seenEmails.add(email);\n uniqueItems.push(item);\n }\n}\n\n// Return the list of unique items\nreturn uniqueItems;"
},
"typeVersion": 2
},
{
"id": "c81e0509-18cf-4a8b-90e2-f0d9c9bbc7de",
"name": "Append or update row in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
2496,
208
],
"parameters": {
"columns": {
"value": {},
"schema": [],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "REPLACE_WITH_GOOGLSHEET_SHEET_ID"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "REPLACE_GOOGLESHEET_SPREADSHEET_ID"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "7cd89cfa-069a-4cb1-8092-27d37b9b90ad",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2096,
80
],
"parameters": {
"color": 7,
"width": 224,
"height": 304,
"content": "## Remove Duplicates"
},
"typeVersion": 1
},
{
"id": "ec9d11ad-7a60-409a-ad31-f7e2953999b3",
"name": "Code2",
"type": "n8n-nodes-base.code",
"position": [
1824,
208
],
"parameters": {
"jsCode": "return items.map(item => {\n const domain = item.json.domain;\n\n // Filter personal domains\n const personalDomains = [\n 'gmail.com', 'yahoo.com', 'outlook.com', 'hotmail.com', 'aol.com', 'icloud.com', 'protonmail.com'\n ];\n if (personalDomains.includes(domain)) {\n return null;\n }\n\n // If the item passes the filter, return it\n return item;\n}).filter(Boolean);"
},
"typeVersion": 2
},
{
"id": "97a76ecb-6165-4702-ba57-fb5a2542c059",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1760,
80
],
"parameters": {
"color": 7,
"width": 224,
"height": 304,
"content": "## Filter out the Personal emails\n(i.e. name@gmail.com)"
},
"typeVersion": 1
},
{
"id": "d1c42689-7256-409b-8972-bb1c69412b11",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
176,
-528
],
"parameters": {
"width": 512,
"height": 1344,
"content": "## Reverse Outreach\n\nThis workflow allows users to extract potential leads sitting on their inboxes. The idea of a reverse outreach is based on the notion that the next big client/customer/partner might be sitting in your inbox waiting to be mined. This automation has two workflows, one that extracts from the historical emails, and the other is a scheduled event, default set to run everyday morning.\nThe workflow intelligently filters out emails from personal domains, system addresses (no-reply, updates), and generic company inboxes (info@, support@). The remaining emails are parsed to extract key information\u2014company name, email address, domain, and subject\u2014which is then stored in a Google Sheets spreadsheet. The Google Sheets node is configured to append or update based on the email address, ensuring that you never store duplicate entries. Finally, you will get a slack message with key information about the lead.\n\n\ud83d\ude80 **How it works**\nManual Trigger: A manual click initiate will fetch all historical emails. the limit set to 500, which you can increase up to 5000\n\nPeriodic Trigger: The workflow is triggered by time, default is set to daily fetch.\n\nCode nodes: Three Code nodes filter the emails based on custom rules - personal domains, system addresses, generic inboxes.\n\nGoogle Sheets: The processed data is sent to a Google Sheets spreadsheet. The append or update operation automatically handles whether to create a new row or update an existing one based on the email address, preventing duplicates.\n\n\ud83d\udd11 **Required Credentials**\nGoogle (Gmail): To access your Gmail account and retrieve email messages.\n\nGoogle Sheets: To connect to your spreadsheet.\n\nSlack Bot: To Send message in a designated slack channel\n\n\ud83d\udee0\ufe0f **Setup Instructions**\nConfigure Gmail Trigger:\n\nConnect your Google cloud account credential in Gmail and Google sheet nodes.\n\nChoose the schedule to run the Email fetch node that periodically watch for new emails.\n\n\nConfigure Code Node:\n\nThis node is pre-configured with the filtering logic. You can customize the lists of personal, blocked, or generic email parts to fit your needs.\n\nConfigure Google Sheets Node:\n\nConnect your Google Sheets credentials.\nCreate a Spreadsheet with the following columns\ncompany_name, email, domain, subject, date_received\n\nEnter the Spreadsheet ID of your target spreadsheet in the Google sheet node along with the Sheet Name (e.g., Leads).\n\nAnd that should do it! Now run the manual trigger workflow and see the lead information showing up in your selected Slack Channel and also in the populated google sheet.\n"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "1e2dfcca-3c26-44dc-90cd-22fc7eccbe0c",
"connections": {
"Code": {
"main": [
[
{
"node": "Code2",
"type": "main",
"index": 0
}
]
]
},
"Code1": {
"main": [
[
{
"node": "Append or update row in sheet",
"type": "main",
"index": 0
}
]
]
},
"Code2": {
"main": [
[
{
"node": "Code1",
"type": "main",
"index": 0
}
]
]
},
"Get many messages": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Gmail Trigger (Real-time)": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Append or update row in sheet": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
},
"Manual Trigger (Historical Run)": {
"main": [
[
{
"node": "Get many messages",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
gmailOAuth2googleSheetsOAuth2ApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow allows users to extract potential leads from their inboxes. The idea of a reverse outreach is based on the notion that the next big client/customer/partner might be sitting in your inbox waiting to be mined. This automation has two workflows, one that extracts from…
Source: https://n8n.io/workflows/7752/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Find companies similar to your best clients using PredictLeads, enrich each with news, hiring, and tech signals, then score them 0–100 for outreach priority.
This n8n template automates finding roofing contractors in any city using Google Maps. It deep-scrapes listings via ScrapeOps Proxy, deduplicates results against Google Sheets, saves fresh leads, and
This n8n template automates the generation of local business leads by scraping Google Maps. It goes beyond basic search results by visiting individual business pages to extract detailed contact inform
This repository contains an SLA-based lead routing workflow built in n8n, designed to ensure fast lead response, fair sales distribution, and controlled escalation without relying on a full CRM system
Automatically qualify, score, and route inbound B2B leads using GPT-4o-mini — no manual review needed.