AutomationFlowsMarketing & Ads › Automate Personalized Email Campaigns with Google Docs, Sheets and SMTP

Automate Personalized Email Campaigns with Google Docs, Sheets and SMTP

ByStéphane Heckel @stephaneheckel on n8n.io

Automate personalized email campaigns using a Google Sheets contact list, a Google Docs template, and SMTP delivery. Google Docs is used as the email template with variables: , , , . Google Sheet contains your list of recipients (one per row). For each contact, the workflow…

Cron / scheduled trigger★★★★☆ complexity21 nodesGoogle SheetsGoogle DocsEmail Send
Marketing & Ads Trigger: Cron / scheduled Nodes: 21 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #7065 — we link there as the canonical source.

This workflow follows the Emailsend → Google Sheets 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 →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "3cc80b37-988e-4111-b9b4-8712a31fe400",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2320,
        -16
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "seconds"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f93e671e-f82d-453c-a734-fb144af0c090",
      "name": "contacts",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1872,
        80
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1877973285,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1VPUd7xbigvW4dIjyvsSRn3Y3jpITjyZneCIEU0HK_Hw/edit#gid=1877973285",
          "cachedResultName": "contacts"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.googlesheetid }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "bed36d9b-defc-46ce-a6cc-aa8efdddfcc4",
      "name": "template",
      "type": "n8n-nodes-base.googleDocs",
      "position": [
        -1760,
        -96
      ],
      "parameters": {
        "operation": "get",
        "documentURL": "={{ $json.googledocid }}"
      },
      "credentials": {
        "googleDocsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2
    },
    {
      "id": "2fb0d6e9-2692-4c4d-b6a7-4d27ba467ef2",
      "name": "updatebody",
      "type": "n8n-nodes-base.set",
      "position": [
        -1184,
        -16
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e9ac9b39-f81e-4754-b978-bb8126c2c846",
              "name": "email",
              "type": "string",
              "value": "={{ $json.email }}"
            },
            {
              "id": "c9f391a2-4017-40f3-9222-cfd79135ecfd",
              "name": "template",
              "type": "string",
              "value": "={{ $json.content\n.replaceAll(/\\{\\{ ?email ?\\}\\}/gm, $json[\"email\"])\n.replaceAll(/\\{\\{ ?company ?\\}\\}/gm, $json[\"company\"])\n.replaceAll(/\\{\\{ ?firstname ?\\}\\}/gm, $json[\"firstname\"])\n.replaceAll(/\\{\\{ ?lastname ?\\}\\}/gm, $json[\"lastname\"]); }}\n\n\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "4453d3d8-65d3-4b5c-8083-9c686be44678",
      "name": "settings",
      "type": "n8n-nodes-base.set",
      "position": [
        -2096,
        -16
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "0cc310e3-f3bb-4347-9d4f-275c00f0f9ac",
              "name": "emailsubject",
              "type": "string",
              "value": "Emailing Template - Email Subject"
            },
            {
              "id": "c17d8bb6-c29f-48c6-830f-acd350617810",
              "name": "googledocid",
              "type": "string",
              "value": "enter_your_googledocID_here"
            },
            {
              "id": "4b74909d-6a40-422f-9d5a-1d72f5577f3f",
              "name": "googlesheetid",
              "type": "string",
              "value": "enter_your_googlesheetID_here"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "806753db-3de0-41e2-b43b-aca9dc5f548c",
      "name": "sendemail",
      "type": "n8n-nodes-base.emailSend",
      "notes": "OVHCloud",
      "position": [
        -720,
        0
      ],
      "parameters": {
        "text": "={{ $json.template }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $('settings').item.json.emailsubject }}",
        "toEmail": "={{ $json.email }}",
        "fromEmail": "myfirstname / mycompany<user@example.com>",
        "emailFormat": "text"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.1
    },
    {
      "id": "bd58504e-6a1e-46fd-b892-03853b0ab89b",
      "name": "notemailed",
      "type": "n8n-nodes-base.filter",
      "position": [
        -1648,
        80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "4b68ad20-a8f8-4d5c-91fe-d4009455bcb3",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.process }}",
              "rightValue": ""
            },
            {
              "id": "fc31006b-9dbe-43a9-ae8a-5389b8379f34",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.err }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "fe137938-de01-4542-bbe1-7d605c4ca372",
      "name": "updatecontacts",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "row_number (using to match)\n{{ $('contacts').item.json.row_number }}\n\nprocess\n{{ $now.format('yyyy-MM-dd, hh:mm:ss a') }}",
      "position": [
        -496,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "process": "={{ $now.format('yyyy-MM-dd, hh:mm:ss a') }}",
            "row_number": "={{ $('contacts').item.json.row_number }}"
          },
          "schema": [
            {
              "id": "email",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "firstname",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "firstname",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lastname",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "lastname",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "company",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "company",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "process",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "process",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "err",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "err",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "contacts"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('settings').item.json.googlesheetid }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "10b636a0-63b8-4215-956f-d66606ae51eb",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -960,
        -16
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "c1a3e48d-3447-400f-a366-447fb126fb08",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        -272,
        80
      ],
      "parameters": {
        "amount": 20
      },
      "typeVersion": 1.1
    },
    {
      "id": "463a82ed-2515-4e29-98c8-a091ec62abdf",
      "name": "No Operation, do nothing",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -736,
        -208
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "bc87d9fe-f2d8-4488-b513-9d54d45b989f",
      "name": "merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1424,
        -16
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3
    },
    {
      "id": "706fec26-39d6-4517-849c-bd7ff52c735e",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 176,
        "height": 188,
        "content": "SMTP providers (like OVHcloud) have quota. \nEx: 200 emails / hour  means you can send 3 emails per minute maximum. So, a Wait Amount of 20 seconds is suggested\n"
      },
      "typeVersion": 1
    },
    {
      "id": "982f0c8d-1cf1-49bf-9a93-0076312c8747",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -528,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 192,
        "height": 188,
        "content": "*row_number* is used for the matching and should contain {{ $('contacts').item.json.row_number }}\n\n*process* is set with {{ $now.format('yyyy-MM-dd, hh:mm:ss a') }}"
      },
      "typeVersion": 1
    },
    {
      "id": "ed4175e8-5d53-4520-83dc-83f098b4678b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -752,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 192,
        "height": 188,
        "content": "Update with your own SMTP details and adapt the \"From Email\" field"
      },
      "typeVersion": 1
    },
    {
      "id": "ac803816-b96d-4d3f-b5e9-ab90c0f6f361",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1232,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 208,
        "height": 444,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nReplace Google Docs template\n{{firstname}}\n{{lastname}} {{company}}\n{{email}}\nwith real content from the Google Sheet\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "1aa209a1-6e65-43e8-a8e3-bb5f539b6702",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1696,
        208
      ],
      "parameters": {
        "color": 7,
        "width": 192,
        "height": 144,
        "content": "We only select rows with *process* and *err* columns = empty"
      },
      "typeVersion": 1
    },
    {
      "id": "6b7d9070-e448-437c-9ae0-e52b2a18dcd0",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1808,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 192,
        "height": 412,
        "content": "Your Google Docs template containing the message with the \n{{firstname}}\n{{lastname}} {{company}}\n{{email}}\nvariables\n\n[Google Docs Template](https://docs.google.com/document/d/1sR1Mjee0heur6CgEV_ssYzOUbFNaDnyEnPSt1CFEMWI/edit?usp=sharing)\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "93ed8856-43da-447c-a85b-a24553468361",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2128,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 192,
        "height": 192,
        "content": "Set the Subject of your emailing campaign, define the Google Docs & Sheet ID you want to use"
      },
      "typeVersion": 1
    },
    {
      "id": "1213bc1e-1ce4-4ec0-af5e-53875f2684d9",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1920,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 192,
        "height": 80,
        "content": "\n[Google Sheet Template](https://docs.google.com/spreadsheets/d/1mFKp3wmbV9qp2tpGGsN72zdiC32y8H1nhjdgP885y-U/edit?usp=sharing)\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "87eddad8-8c0a-41a0-8f5d-1d2d323382ee",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2368,
        400
      ],
      "parameters": {
        "color": 7,
        "width": 1376,
        "height": 288,
        "content": "# Emailing using Google Sheet, Google Docs and SMTP\nThis is a simple system to automatise an emailing.\n\n## How it works\n\n- The Google Docs template is your email to be sent and contains the message and variables (firstname, lastname, company, email).\n- The Google Sheet is your list of contacts.\n- For each contact, we create the final email, we send it using SMTP protocol (with a Batch Size of 1) and we manage the SMTP quota.\n- The **process** column of the Google Sheet is updated with date/time after email is sent.\n\n\n## How to use\n1. Download the [Google Docs Template](https://docs.google.com/document/d/1sR1Mjee0heur6CgEV_ssYzOUbFNaDnyEnPSt1CFEMWI/edit?usp=sharing) and [Google Sheet Template](https://docs.google.com/spreadsheets/d/1mFKp3wmbV9qp2tpGGsN72zdiC32y8H1nhjdgP885y-U/edit?usp=sharing). Identify the ID of both documents in your own environment.\n   - The ID is the `string` between d/ and /edit ... spreadsheets/d/`1mFKp3wmba1b2c3N72zdiC32y8H1nhjdgP85y-U`/edit?gid=1234\n\n2. Define the Subject of your emailing campaing and setup the Google Sheet & Docs ID in the `settings` node\n\n3. Configure your Google credentials and SMTP parameters\n\n4. Update both Google Docs & Sheet with your own message and list of contacts\n   - The Google Docs template can contain the following variables `{{firstname}}` `{{lastname}}` `{{company}}` `{{email}}`\n   - The header of the Google Sheet should contain the following columns **email**, **firstname**, **lastname**, **company**, **process**, **err**\n\n## Requirements\n- **Google credentials** to access your documents\n- **SMTP** server, up and running. Update the `Wait` node with what works for you\n- **n8n version** this workflow was tested on 1.105.2 (Ubuntu)\n\n## Need Help?\n\nContact me on [LinkedIn](https://www.linkedin.com/in/stephaneheckel/) or ask in the [Forum](https://community.n8n.io/)!\n\n\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "merge": {
      "main": [
        [
          {
            "node": "updatebody",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "contacts": {
      "main": [
        [
          {
            "node": "notemailed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "settings": {
      "main": [
        [
          {
            "node": "template",
            "type": "main",
            "index": 0
          },
          {
            "node": "contacts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "template": {
      "main": [
        [
          {
            "node": "merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "sendemail": {
      "main": [
        [
          {
            "node": "updatecontacts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "notemailed": {
      "main": [
        [
          {
            "node": "merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "updatebody": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "updatecontacts": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "sendemail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "settings",
            "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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Automate personalized email campaigns using a Google Sheets contact list, a Google Docs template, and SMTP delivery. Google Docs is used as the email template with variables: , , , . Google Sheet contains your list of recipients (one per row). For each contact, the workflow…

Source: https://n8n.io/workflows/7065/ — original creator credit. Request a take-down →

More Marketing & Ads workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Marketing & Ads

Automatización de Email Marketing. Uses googleSheets, emailSend. Scheduled trigger; 4 nodes.

Google Sheets, Email Send
Marketing & Ads

Workflow-Lead-Summary. Uses googleSheets, emailSend. Scheduled trigger; 3 nodes.

Google Sheets, Email Send
Marketing & Ads

This workflow runs on scheduled weekly and monthly triggers to generate unified marketing performance reports. It processes multiple websites by collecting analytics data, paid ads performance, and CR

Gmail, Google Sheets, Google Analytics +3
Marketing & Ads

Goal: Get Reddit posts from specific subreddits, filter those mentioning freelance/gigs and n8n, extract top-level comments, remove mod replies, and store everything into Google Sheets.

HTTP Request, Reddit, Google Sheets
Marketing & Ads

This workflow is designed to manage the assignment and validation of unique QR code coupons within a lead generation system with SuiteCRM.

HTTP Request, Form Trigger, Google Sheets +1