This workflow corresponds to n8n.io template #10972 — we link there as the canonical source.
This workflow follows the Gmail → Google Drive 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": "JzBm7zPFj4AURwU2",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Automated Outreach Emails with Gmail & Google Sheets Safety Controls",
"tags": [
{
"id": "9fXLydrXFV9BUci9",
"name": "JOB",
"createdAt": "2025-12-15T14:02:22.458Z",
"updatedAt": "2025-12-15T14:02:22.458Z"
},
{
"id": "VI8W1vANQZgRV7M5",
"name": "SHEETS",
"createdAt": "2025-11-25T20:39:12.984Z",
"updatedAt": "2025-11-25T20:39:12.984Z"
},
{
"id": "vLLcwhiSm3lgtjpI",
"name": "PORTFOLIO",
"createdAt": "2025-10-30T14:01:29.686Z",
"updatedAt": "2025-10-30T14:01:29.686Z"
}
],
"nodes": [
{
"id": "89b64ec1-ea43-4607-b45d-f5bdc31e4d89",
"name": "Get Contacts with Emails",
"type": "n8n-nodes-base.code",
"position": [
-2736,
352
],
"parameters": {
"jsCode": "return items.filter(item => {\n const email = item.json.EMAIL?.trim();\n const status = item.json.status?.toLowerCase().trim();\n\n const invalidStatuses = ['hot lead', 'unresponsive', 'spam', 'do not contact'];\n\n return email && !invalidStatuses.includes(status);\n});\n"
},
"retryOnFail": true,
"typeVersion": 2
},
{
"id": "98cded50-307c-4c0b-860e-26e9344be990",
"name": "Wait",
"type": "n8n-nodes-base.wait",
"position": [
-528,
-112
],
"parameters": {
"amount": "={{ Number($json.delay) / 1000 }}"
},
"retryOnFail": true,
"typeVersion": 1.1
},
{
"id": "b9c9ef0d-4459-4ed7-a32a-2ed884be31be",
"name": "Initial Email NOT Sent?",
"type": "n8n-nodes-base.if",
"position": [
-1104,
240
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "2e82cac2-bf34-4184-926b-9e6a160a4bc9",
"operator": {
"type": "string",
"operation": "empty",
"singleValue": true
},
"leftValue": "={{ $json.STATUS }}",
"rightValue": "SENT "
}
]
},
"looseTypeValidation": true
},
"retryOnFail": true,
"typeVersion": 2.2
},
{
"id": "2ebd600b-a5b0-46e0-bc76-5e81c88a314c",
"name": "Check Daily Email Counter",
"type": "n8n-nodes-base.code",
"position": [
-208,
-112
],
"parameters": {
"jsCode": "// === Daily Email Counter with Weekend Exclusion & Category Tracking ===\n\n// \u2705 1. Get today's date and day of week\nconst now = new Date();\nconst today = now.toISOString().split('T')[0];\nconst day = now.getDay(); // 0 = Sunday, 6 = Saturday\n\n// \u2705 2. Skip weekends automatically\nif (day === 0 || day === 6) {\n console.log(\"\ud83d\udeab Weekend detected. Skipping all email sends today.\");\n return []; // Stop workflow on weekends\n}\n\n// \u2705 3. Set your total daily Gmail quota\nconst totalLimit = 100; // adjust if needed\n\n// \u2705 4. Access persistent storage (shared across all workflows)\nconst data = $getWorkflowStaticData('global');\n\n// \u2705 5. Reset if new day\nif (!data.sentCountDate || data.sentCountDate !== today) {\n data.sentCountDate = today;\n data.sentCount = 0;\n data.breakdown = { initial: 0, retarget1: 0, retarget2: 0, reply: 0 };\n}\n\n// \u2705 6. Read email type passed from the Set node\nconst emailType = $json.emailType || 'unknown';\n\n// \u2705 7. Stop all if total daily quota is reached\nif (data.sentCount >= totalLimit) {\n console.log(`\ud83d\udeab Daily Gmail quota reached (${data.sentCount}/${totalLimit}).`);\n return [];\n}\n\n// \u2705 8. Define category-specific ratios (adjust to your liking)\nconst ratios = {\n initial: 0.55, // 55% for new hot leads\n retarget1: 0.20, // 20% for first follow-up\n retarget2: 0.15, // 15% for second follow-up\n reply: 0.10, // 10% for ongoing replies\n};\n\n// \u2705 9. Compute local quota per category\nconst localLimit = Math.floor(totalLimit * (ratios[emailType] || 0.25));\n\n// \u2705 10. Skip sending if that category is full\nif (data.breakdown[emailType] >= localLimit) {\n console.log(`\u23f8 ${emailType} quota full (${localLimit} max).`);\n return [];\n}\n\n// \u2705 11. Increment counters\ndata.sentCount += 1;\nif (!data.breakdown[emailType]) data.breakdown[emailType] = 0;\ndata.breakdown[emailType] += 1;\n\n// \u2705 12. Log updates\nconsole.log(`\ud83d\udce9 Sent #${data.sentCount}/${totalLimit} | Type: ${emailType}`);\nconsole.log(`\ud83d\udcca Breakdown: ${JSON.stringify(data.breakdown)}`);\n\n// \u2705 13. Allow sending to continue\nreturn items;"
},
"retryOnFail": true,
"typeVersion": 2
},
{
"id": "ec30e632-5d3b-4d8f-aa8b-a84f606d445c",
"name": "Initial",
"type": "n8n-nodes-base.set",
"position": [
-352,
-112
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "010781bc-0f40-4e8a-9146-e8b202843b03",
"name": "emailType",
"type": "string",
"value": "\"initial\""
}
]
},
"includeOtherFields": true
},
"retryOnFail": true,
"typeVersion": 3.4
},
{
"id": "2e3376fb-07da-406e-802b-b767d67f35b1",
"name": "Is SPAM?",
"type": "n8n-nodes-base.if",
"position": [
-2000,
336
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "2e82cac2-bf34-4184-926b-9e6a160a4bc9",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.SPAM }}",
"rightValue": "YES"
}
]
},
"looseTypeValidation": true
},
"retryOnFail": true,
"typeVersion": 2.2
},
{
"id": "0d1f3a64-59f0-4d38-8a85-ead4b07acb75",
"name": "Initial Email",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-880,
-128
],
"parameters": {
"options": {}
},
"retryOnFail": true,
"typeVersion": 3
},
{
"id": "3a392018-8b0c-4f68-959d-b5bb9bb69bae",
"name": "Human wait time1",
"type": "n8n-nodes-base.code",
"position": [
-688,
-112
],
"parameters": {
"jsCode": "// Generate a random delay between 0 and 60 seconds (0\u20132 minutes)\nconst delay = Math.floor(Math.random() * 121); // random 0\u201360 seconds\n\n// Return delay in milliseconds so n8n understands\nreturn {\n delay: delay * 1000\n};"
},
"retryOnFail": true,
"typeVersion": 2
},
{
"id": "8c66e9bc-d6a7-46d7-807d-a4c6c0a58a36",
"name": "VALID EMAIL",
"type": "n8n-nodes-base.code",
"position": [
-2512,
352
],
"parameters": {
"jsCode": "// Full email cleanup + validation + precise block rules\nreturn items.map(item => {\n let email = (item.json.EMAIL || \"\").trim().toLowerCase();\n email = email.replace(/[.,]+$/, \"\"); // remove trailing dots/commas\n item.json.EMAIL = email;\n\n // Step 1: Strict syntax validation\n const validEmail =\n /^(?!.*\\.\\.)([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\\.[A-Za-z]{2,})$/;\n const match = email.match(validEmail);\n const domain = match ? match[2] : \"\";\n\n // Step 2: Blocked full domains\n const blockedDomains = [\n \"coingecko.com\",\n \"coinmarketcap.com\",\n \"pinksale.finance\",\n \"pump.fun\",\n \"domain.com\",\n \"mysite.com\",\n \"facebook.com\",\n \"instagram.com\",\n \"linkedin.com\",\n \"twitter.com\",\n \"binance.com\",\n \"mexc.com\",\n \"tiktok.com\",\n ];\n\n // Step 3: Blocked partial or exact keyword matches\n // \"@x.com\" specifically blocks real X (Twitter) addresses, not random \".x.com\" strings\n const blockedKeywords = [\n \"pumpfun\", // covers subdomains like \"team.pumpfun.io\"\n \"@x.com\", // only blocks if \"@x.com\" appears directly in email\n ];\n\n const isBlocked =\n blockedDomains.includes(domain) ||\n blockedKeywords.some(k => email.includes(k.toLowerCase()));\n\n const isValidFormat = !!match;\n const isValid = isValidFormat && !isBlocked;\n\n // Step 4: Explanation fields\n item.json.valid = isValid;\n item.json.blocked = isBlocked;\n item.json.reason = !isValidFormat\n ? \"Invalid format\"\n : isBlocked\n ? \"Blocked keyword/domain\"\n : \"Valid email\";\n\n return item;\n});"
},
"retryOnFail": true,
"typeVersion": 2
},
{
"id": "46691763-e027-45cd-861d-1e285e0aa37f",
"name": "VALID EMAIL?",
"type": "n8n-nodes-base.if",
"position": [
-2288,
352
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "4624283a-cb44-445c-a6f2-129fe51f7508",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.valid }}",
"rightValue": true
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "f4f8b2cc-9611-4830-93ce-b4ad24d02ff0",
"name": "undelivered email?",
"type": "n8n-nodes-base.if",
"position": [
-1504,
432
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "3f1b5f23-fc22-45a3-820f-a9dbb78e44ec",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json[\"EMAIL VERIFIED\"] }}",
"rightValue": "REACHABLE"
}
]
},
"looseTypeValidation": true
},
"retryOnFail": true,
"typeVersion": 2.2
},
{
"id": "9c2c47fb-d6e3-41bd-bd84-e5ef17efb7f2",
"name": "EMAIL VERIFICATION DONE?",
"type": "n8n-nodes-base.if",
"position": [
-1664,
592
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d69349ac-3f32-4ecd-a3a6-15a78c65062b",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json['EMAIL VERIFIED'] }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "e9e78cba-c918-4265-bd78-b8c8311baac5",
"name": "ONLY CHECK EMAIL NEEDED",
"type": "n8n-nodes-base.filter",
"position": [
144,
-112
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "f513ee99-602f-4bc8-8a99-3716f60a626a",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.EMAIL }}",
"rightValue": "={{ $('Initial Email').item.json.EMAIL }}"
}
]
},
"looseTypeValidation": true
},
"retryOnFail": true,
"typeVersion": 2.2
},
{
"id": "8ee0320f-c939-4a57-bb21-8cd25a6feeb7",
"name": "9am Daily",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-3440,
16
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 9
}
]
}
},
"retryOnFail": true,
"typeVersion": 1.2
},
{
"id": "49ce39e0-731d-4ab3-bea8-99de19504bd7",
"name": "SUBJECT LINES",
"type": "n8n-nodes-base.code",
"position": [
384,
-112
],
"parameters": {
"jsCode": "function pickRandom(arr) {\n return arr[Math.floor(Math.random() * arr.length)];\n}\n\nreturn items.map(item => {\n /* -------- SUBJECT LINES -------- */\n const subjectPool = [\n \"Quick question\",\n \"Testing a new outreach workflow\",\n \"Short introduction\",\n \"Reaching out briefly\",\n \"Exploring a quick connection\"\n ];\n\n /* -------- OPENING LINES -------- */\n const openingLinePool = [\n \"I hope you\u2019re having a great day.\",\n \"Just a quick note to introduce myself.\",\n \"I wanted to briefly reach out and say hello.\"\n ];\n\n /* -------- MAIN BODY (SALES / TEST) -------- */\n const bodyPool = [\n \"This is a short outreach message as part of testing a lightweight email automation workflow. No action is required \u2014 just validating delivery and formatting.\",\n \"I\u2019m currently testing a simple outreach system designed for clean, low-volume communication. This message is part of that test.\",\n \"This email is part of a small test to confirm that a new outreach workflow is working as expected.\"\n ];\n\n /* -------- CLOSING LINES -------- */\n const closingLinePool = [\n \"Thanks for your time.\",\n \"Appreciate you reading this.\"\n ];\n\n /* -------- SIGNATURE -------- */\n const senderName = item.json[\"Sender Name\"] || \"Your Name\";\n\n const signature = `\n<p style=\"margin-top:14px;font-family:Arial,Helvetica,sans-serif;font-size:14px;\">\nYours sincerely,<br>\n<strong>${senderName}</strong>\n</p>\n`;\n\n return {\n json: {\n ...item.json,\n Subject: pickRandom(subjectPool),\n Greeting: `Hi ${item.json.Name || \"there\"},`,\n Opening: pickRandom(openingLinePool),\n Body: pickRandom(bodyPool),\n Closing: pickRandom(closingLinePool),\n Signature: signature\n }\n };\n});\n"
},
"retryOnFail": true,
"typeVersion": 2
},
{
"id": "db293a22-876e-4c0c-83f4-8ed39a7ec855",
"name": "Download file",
"type": "n8n-nodes-base.googleDrive",
"position": [
832,
-128
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "url",
"value": ""
},
"options": {},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "68833003-73d3-4b46-b63b-0cabbe06e0a5",
"name": "EMAILS SHEET",
"type": "n8n-nodes-base.googleSheets",
"position": [
-3120,
352
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 1160858465,
"cachedResultUrl": "",
"cachedResultName": "PORTFOLIO"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1ua7aVmykESCwx6JBHBZHDmNyg7tmTkGnaL4WeoptHSE",
"cachedResultUrl": "",
"cachedResultName": "JOB"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "5a3f979e-0daf-4f6c-8c57-27729e7ffc8c",
"name": "10 SECONDS WAIT",
"type": "n8n-nodes-base.wait",
"position": [
-2944,
352
],
"parameters": {
"amount": 10
},
"retryOnFail": true,
"typeVersion": 1.1
},
{
"id": "f736ceff-a4b3-4a36-bfe5-58817b091621",
"name": "Initial Email NOT Sent? SECOND CHECK",
"type": "n8n-nodes-base.if",
"position": [
624,
-112
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "2e82cac2-bf34-4184-926b-9e6a160a4bc9",
"operator": {
"type": "string",
"operation": "empty",
"singleValue": true
},
"leftValue": "={{ $json.STATUS }}",
"rightValue": "SENT "
}
]
},
"looseTypeValidation": true
},
"retryOnFail": true,
"typeVersion": 2.2
},
{
"id": "b4be98a9-6680-4297-b4d3-991ae37280e6",
"name": "UPDATE EMAIL SHEET",
"type": "n8n-nodes-base.googleSheets",
"position": [
1200,
-48
],
"parameters": {
"columns": {
"value": {
"SENT": "={{ 'SENT \u2705' }}",
"EMAIL": "={{ $('Initial Email NOT Sent? SECOND CHECK').item.json.EMAIL }}",
"DATE SENT": "={{ new Date().toISOString().split('T')[0] }}"
},
"schema": [
{
"id": "EMAIL",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "EMAIL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Name",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "EMAIL VERIFIED",
"type": "string",
"display": true,
"required": false,
"displayName": "EMAIL VERIFIED",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "EMAIL SMTP VERIFIED",
"type": "string",
"display": true,
"required": false,
"displayName": "EMAIL SMTP VERIFIED",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "EMAIL SUGGESTION",
"type": "string",
"display": true,
"required": false,
"displayName": "EMAIL SUGGESTION",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "EMAIL TYPE",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "EMAIL TYPE",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "SENT",
"type": "string",
"display": true,
"required": false,
"displayName": "SENT",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "DATE SENT",
"type": "string",
"display": true,
"required": false,
"displayName": "DATE SENT",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"EMAIL"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 1160858465,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ua7aVmykESCwx6JBHBZHDmNyg7tmTkGnaL4WeoptHSE/edit#gid=1160858465",
"cachedResultName": "PORTFOLIO"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1ua7aVmykESCwx6JBHBZHDmNyg7tmTkGnaL4WeoptHSE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ua7aVmykESCwx6JBHBZHDmNyg7tmTkGnaL4WeoptHSE/edit?usp=drivesdk",
"cachedResultName": "JOB"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "cea79305-c6a0-442e-93f8-aab30b9efe2f",
"name": "SEND INITIAL EMAIL",
"type": "n8n-nodes-base.gmail",
"maxTries": 2,
"position": [
976,
-128
],
"parameters": {
"sendTo": "={{ $json.EMAIL }}",
"message": "=Hi {{ $json.Name || \"there\" }},\n<br><br>\n{{ $json.Opening }}\n<br><br>\n{{ $json.Body }}\n<br><br>\n{{ $json.Closing }}\n\nKindly find attached a sample image.\n<br><br>\n{{ $json.Signature }}\n",
"options": {
"senderName": "=Your Name - Highlight",
"attachmentsUi": {
"attachmentsBinary": [
{}
]
},
"appendAttribution": false
},
"subject": "={{ $('SUBJECT LINES').item.json.Subject }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 2.1,
"alwaysOutputData": false
},
{
"id": "95bfba0d-9c27-4bdc-86f6-44a31a20a226",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3824,
-432
],
"parameters": {
"width": 640,
"height": 1184,
"content": "## \ud83d\udce7 AUTOMATED INITIAL COLD OUTREACH EMAILS WITH SAFETY CONTROLS\n\n## HOW?\n\nThis workflow automatically sends personalized emails to contacts\nstored in Google Sheets and NocoDB.\n\nIt includes email validation, spam filtering, daily send limits,\nhuman-like delays, and automatic status updates.\n\n## SETUP STEPS\n1. Connect Google Sheets\n2. Connect Gmail\n3. Connect NocoDB (optional)\n4. Review email limits before activating\n\n\n## GMAIL PAYLOAD\nto: {{ $json.EMAIL }}\nsubject: {{ $('SUBJECT LINES').item.json.Subject }}\n\nHi {{ $json.Name || \"there\" }},\n<br><br>\n{{ $json.Opening }}\n<br><br>\n{{ $json.Body }}\n<br><br>\n{{ $json.Closing }}\n\nKindly find attached a sample image.\n<br><br>\n{{ $json.Signature }}\n\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "f0f07d57-2128-4d2b-9533-816d1fc0b8b1",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3168,
96
],
"parameters": {
"color": 7,
"width": 576,
"height": 656,
"content": "## STEP 1: Get Contats From Sheets\n\nThis step pulls contact records from Google Sheets.\n\n-->Only rows with email addresses are considered.\n\n"
},
"typeVersion": 1
},
{
"id": "22ff2773-9a0d-4eea-8fa1-57a908e7b766",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2544,
96
],
"parameters": {
"color": 7,
"width": 416,
"height": 656,
"content": "## STEP 2: Clean and Validate Emails\n\n\nThis step:\n\u2022 Fixes formatting issues\n\u2022 Blocks known bad domains\n\u2022 Rejects invalid email formats\n\u2022 Flags risky addresses\n\n"
},
"typeVersion": 1
},
{
"id": "88d10059-24f5-454f-b4f7-f6c0de19d7d6",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2080,
96
],
"parameters": {
"color": 7,
"width": 336,
"height": 656,
"content": "## STEP 3: Spam Check\n\n\nThis step checks if a contact is marked as spam\n\n-->Spam records are skipped immediately to protect your email reputation.\n\n\n"
},
"typeVersion": 1
},
{
"id": "16397514-e1b0-4acc-a9f3-b1844e4d71d3",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1712,
96
],
"parameters": {
"color": 7,
"width": 768,
"height": 656,
"content": "## STEP 4: Initial Email Status Check\n\nThis step checks if an initial email was already sent.\n\n-->If yes, the workflow skips the contact to avoid duplicate or accidental re-sending.\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "2a808f7c-9015-4c30-9c32-65fbef9dab08",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-384,
-416
],
"parameters": {
"color": 7,
"width": 304,
"height": 480,
"content": "## STEP 6: Daily Status Check\n\nThis step controls how many emails can be sent per day.\n\nIt:\n\u2022 Blocks sending on weekends\n\u2022 Enforces daily email limits\n\u2022 Tracks counts per email type\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "7bb21639-7b9f-4eab-acc5-ebb971833fba",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-928,
-416
],
"parameters": {
"color": 7,
"width": 528,
"height": 480,
"content": "## STEP 5: Human Like Delay\n\nA random delay is added before sending each email.\n\nThis makes the workflow behave more like a human\nand less like a bot, reducing the risk of spam detection\nor account restrictions.\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "0c731a75-e623-42ff-9112-7cf3bac441ed",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
304,
-416
],
"parameters": {
"color": 7,
"width": 272,
"height": 480,
"content": "## STEP 8: Subject and Content Generation\n\nThis step generates personalized subject lines\nand opening sentences.\n\n\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "23e62390-7138-46e8-b30d-4a571eb10712",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
592,
-416
],
"parameters": {
"color": 7,
"width": 512,
"height": 480,
"content": "## STEP 9: Send Email\n\nThis step sends the actual email using Gmail.\n\nThe email includes:\n\u2022 Personalized subject line\n\u2022 Custom opening\n\u2022 Main message\n\u2022 Signature and links\n\nAttachments can also be included here.\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "e813f237-ae28-4546-8f2a-5a313a0bbb5e",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
1152,
-432
],
"parameters": {
"color": 7,
"height": 544,
"content": "## STEP 10: Update Sheet\n\nAfter an email is sent, this step updates Google Sheets.\n\nIt records:\n\u2022 Email sent status\n\u2022 Date sent\n\u2022 Email address\n\nThis ensures accurate tracking\nand prevents duplicate sends.\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "dbb6e068-bfda-433d-aa6e-9e78995f2944",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
-64,
-416
],
"parameters": {
"color": 7,
"width": 352,
"height": 480,
"content": "## STEP 7: Initial Email Check\n\nThis step confirms the correct email record is being processed.\n\n-->It prevents mismatched data before generating content or sending emails.\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "4349a640-c900-443a-b6f7-c18d143fd69c",
"name": "INITIAL EMAIL SHEET",
"type": "n8n-nodes-base.googleSheets",
"position": [
-32,
-112
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 1160858465,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ua7aVmykESCwx6JBHBZHDmNyg7tmTkGnaL4WeoptHSE/edit#gid=1160858465",
"cachedResultName": "PORTFOLIO"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1ua7aVmykESCwx6JBHBZHDmNyg7tmTkGnaL4WeoptHSE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ua7aVmykESCwx6JBHBZHDmNyg7tmTkGnaL4WeoptHSE/edit?usp=drivesdk",
"cachedResultName": "JOB"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "7f2db0d1-4524-44f1-b11c-b0feb4d385a2",
"name": "Has Name?",
"type": "n8n-nodes-base.if",
"position": [
-1296,
336
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "36c386b4-e586-4374-997d-f7879f6d8670",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.Name }}",
"rightValue": ""
}
]
},
"looseTypeValidation": true
},
"retryOnFail": true,
"typeVersion": 2.2
}
],
"active": false,
"settings": {
"timezone": "Africa/Lagos",
"callerPolicy": "workflowsFromSameOwner",
"errorWorkflow": "",
"availableInMCP": false,
"executionOrder": "v1",
"saveDataSuccessExecution": "all"
},
"versionId": "1a6195c4-2b71-4adc-bffc-5e1d6e7bc2b3",
"connections": {
"Wait": {
"main": [
[
{
"node": "Initial",
"type": "main",
"index": 0
}
]
]
},
"Initial": {
"main": [
[
{
"node": "Check Daily Email Counter",
"type": "main",
"index": 0
}
]
]
},
"Is SPAM?": {
"main": [
[],
[
{
"node": "EMAIL VERIFICATION DONE?",
"type": "main",
"index": 0
}
]
]
},
"9am Daily": {
"main": [
[
{
"node": "EMAILS SHEET",
"type": "main",
"index": 0
}
]
]
},
"Has Name?": {
"main": [
[
{
"node": "Initial Email NOT Sent?",
"type": "main",
"index": 0
}
]
]
},
"VALID EMAIL": {
"main": [
[
{
"node": "VALID EMAIL?",
"type": "main",
"index": 0
}
]
]
},
"EMAILS SHEET": {
"main": [
[
{
"node": "10 SECONDS WAIT",
"type": "main",
"index": 0
}
]
]
},
"VALID EMAIL?": {
"main": [
[
{
"node": "Is SPAM?",
"type": "main",
"index": 0
}
],
[]
]
},
"Download file": {
"main": [
[
{
"node": "SEND INITIAL EMAIL",
"type": "main",
"index": 0
}
]
]
},
"Initial Email": {
"main": [
[],
[
{
"node": "Human wait time1",
"type": "main",
"index": 0
}
]
]
},
"SUBJECT LINES": {
"main": [
[
{
"node": "Initial Email NOT Sent? SECOND CHECK",
"type": "main",
"index": 0
}
]
]
},
"10 SECONDS WAIT": {
"main": [
[
{
"node": "Get Contacts with Emails",
"type": "main",
"index": 0
}
]
]
},
"Human wait time1": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
},
"SEND INITIAL EMAIL": {
"main": [
[
{
"node": "UPDATE EMAIL SHEET",
"type": "main",
"index": 0
}
]
]
},
"UPDATE EMAIL SHEET": {
"main": [
[
{
"node": "Initial Email",
"type": "main",
"index": 0
}
]
]
},
"undelivered email?": {
"main": [
[
{
"node": "Has Name?",
"type": "main",
"index": 0
}
],
[]
]
},
"INITIAL EMAIL SHEET": {
"main": [
[
{
"node": "ONLY CHECK EMAIL NEEDED",
"type": "main",
"index": 0
}
]
]
},
"Initial Email NOT Sent?": {
"main": [
[
{
"node": "Initial Email",
"type": "main",
"index": 0
}
],
[]
]
},
"ONLY CHECK EMAIL NEEDED": {
"main": [
[
{
"node": "SUBJECT LINES",
"type": "main",
"index": 0
}
]
]
},
"EMAIL VERIFICATION DONE?": {
"main": [
[
{
"node": "undelivered email?",
"type": "main",
"index": 0
}
],
[]
]
},
"Get Contacts with Emails": {
"main": [
[
{
"node": "VALID EMAIL",
"type": "main",
"index": 0
}
]
]
},
"Check Daily Email Counter": {
"main": [
[
{
"node": "INITIAL EMAIL SHEET",
"type": "main",
"index": 0
}
]
]
},
"Initial Email NOT Sent? SECOND CHECK": {
"main": [
[
{
"node": "Download file",
"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.
gmailOAuth2googleApigoogleDriveOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This n8n workflow sends personalized outreach emails automatically while enforcing strict safety rules such as email validation, spam checks, daily limits, and human-like delays.
Source: https://n8n.io/workflows/10972/ — 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.
Automatically extract structured information from emails using AI-powered document analysis. This workflow processes emails from specified domains, classifies them by type, and extracts structured dat
This weekly workflow helps you stay on top of SEO visibility losses by automatically detecting when your previously strong keywords fall out of Google’s top 10 results.
What This Flow Does
This workflow automates the backup of all your n8n workflows to a specified Google Drive folder. It operates in two main phases: Orchestration (Scheduled Task): The workflow is initiated by a Schedule
This n8n template allows you to automatically monitor your company's budget by comparing live Bexio accounting data against targets defined in Google Sheets, sending automated weekly email reports. It