---
title: "Proven n8n Cold Email Automation: 7-Step Workflow Guide"
slug: "n8n-cold-email-automation-7-step-workflow"
published: "2026-03-12"
updated: "2026-04-06"
validated: "2026-02-24"
categories:
  - "Tools"
tags:
  - "n8n cold email automation"
  - "n8n workflow"
  - "cold email automation"
  - "Google Sheets Gmail integration"
  - "random delay n8n"
  - "email deliverability"
  - "limit node n8n"
  - "schedule trigger n8n"
  - "no-loop automation"
  - "personalized cold email"
  - "n8n tutorial"
llm-intent: "reference"
audience-level: "intermediate"
framework-versions:
  - "n8n@2.x"
  - "google-sheets-api@v4"
  - "gmail-api@v1"
  - "nodejs@18"
status: "stable"
llm-purpose: "n8n cold email automation: set up a daily 20-email workflow using Google Sheets and Gmail with randomized delays, sent-status updates, and a step-by-step…"
llm-prereqs:
  - "Access to n8n"
  - "Access to Google Sheets"
  - "Access to Gmail"
  - "Access to Google OAuth"
  - "Access to Google Workspace"
llm-outputs:
  - "Completed outcome: n8n cold email automation: set up a daily 20-email workflow using Google Sheets and Gmail with randomized delays, sent-status updates, and a step-by-step…"
---

**Summary Triples**
- (workflow, sends, 20 emails per day)
- (Google Sheet, mustContainColumns, Email, Subject, Copy, sentAt, row_number)
- (sentAt, controlsQueueState, empty = queued; set to '1' after send)
- (Schedule Trigger node, shouldBeConfigured, daily at chosen time with correct timezone (e.g., 7am local))
- (Google Sheets node, operation, Get Row(s) to return each row as an item)
- (Limit node, enforces, daily 20-email cap for the run)
- (Delay (randomized), introduces, human-like randomized pauses between sends)
- (Gmail node, requires, Google OAuth credentials with Gmail send scopes)
- (row_number, usedTo, identify and update the exact sheet row after sending)
- (workflow design, avoids, manual for-loops by using n8n's per-item processing)
- (post-send action, updates, sentAt column to mark row as sent and prevent duplicates)
- (timezone misconfig, causes, schedule firing at wrong local time if left as UTC)

### {GOAL}
n8n cold email automation: set up a daily 20-email workflow using Google Sheets and Gmail with randomized delays, sent-status updates, and a step-by-step…

### {PREREQS}
- Access to n8n
- Access to Google Sheets
- Access to Gmail
- Access to Google OAuth
- Access to Google Workspace

### {STEPS}
1. Add a daily schedule trigger
2. Read rows from Google Sheets
3. Filter out already sent rows
4. Limit batch to 20 sends
5. Add randomized wait between sends
6. Send email with Gmail node
7. Update sheet row as sent

<!-- llm:goal="n8n cold email automation: set up a daily 20-email workflow using Google Sheets and Gmail with randomized delays, sent-status updates, and a step-by-step…" -->
<!-- llm:prereq="Access to n8n" -->
<!-- llm:prereq="Access to Google Sheets" -->
<!-- llm:prereq="Access to Gmail" -->
<!-- llm:prereq="Access to Google OAuth" -->
<!-- llm:prereq="Access to Google Workspace" -->
<!-- llm:output="Completed outcome: n8n cold email automation: set up a daily 20-email workflow using Google Sheets and Gmail with randomized delays, sent-status updates, and a step-by-step…" -->

# Proven n8n Cold Email Automation: 7-Step Workflow Guide
> n8n cold email automation: set up a daily 20-email workflow using Google Sheets and Gmail with randomized delays, sent-status updates, and a step-by-step…
Matija Žiberna · 2026-03-12

# How I Automated Cold Email Outreach with n8n and Google Sheets

I had a CSV with emails, subjects, and copy. I needed to send them out daily — 20 at a time — without babysitting the process. My first instinct was to write a for loop. Then n8n showed me I was thinking about it all wrong.

This guide walks you through building a fully automated cold email workflow using n8n, Google Sheets, and Gmail. By the end, you will have a workflow that runs every morning, picks up the next 20 unsent rows from your sheet, sends each email with a human-like random delay, and marks them as sent — all without writing a single loop.

---

## The Sheet Setup

Before touching n8n, get your Google Sheet ready. You need five columns:

**Email, Subject, Copy, sentAt, row_number**

The `sentAt` column is what controls everything. When it is empty, the row is queued. When the workflow sends the email, it sets `sentAt` to `1`. That is your entire state management — dead simple and it works.

The `row_number` column is added automatically by n8n when it reads the sheet. You will use it later to update the correct row after sending.

Fill in your Email, Subject, and Copy rows. Leave `sentAt` blank for everything you want sent.

---

## Building the Workflow

### Step 1: Schedule Trigger

Add a Schedule Trigger node and set it to run daily at 7am. Make sure the timezone in the node matches your local timezone — n8n defaults to UTC and your 7am will fire at the wrong time if you miss this.

### Step 2: Google Sheets — Read Rows

Add a Google Sheets node connected to your spreadsheet. Set the operation to "Get Row(s)" and point it at your sheet. Connect your Google account via OAuth when prompted.

This node returns every row in your sheet as individual items. And here is the thing that changes how you think about n8n entirely.

**n8n does not return an array you need to loop over. Every row becomes its own item, and every node downstream runs once per item automatically.**

There is no for loop. There is no `.map()`. There is no iteration logic to write. You build the workflow for a single item and n8n handles the rest. If 200 rows come through, every node fires 200 times. This is the core mental shift.

### Step 3: Filter — Skip Already Sent

Add a Filter node and set the condition: `sentAt` is not equal to `1`.

This filters out anything already sent and passes only fresh rows downstream. Combined with n8n's per-item execution, only unsent emails continue through the workflow.

### Step 4: Limit — Cap at 20 Per Day

Add a Limit node and set it to 20. Even though your sheet might have hundreds of rows, only the first 20 that pass the filter will continue. This keeps your daily send volume controlled and your domain reputation healthy.

If you have an older Google Workspace account with a solid sending history, 20 is conservative. You can push higher, but start here.

### Step 5: Wait — Random Delay Between Sends

This is the detail that makes your outreach look human rather than mechanical. Add a Wait node and in the amount field, open the expression editor and enter:

```js
{{ Math.floor(Math.random() * 61) + 30 }}
```

This generates a random number between 30 and 90 each time the node runs — meaning each email goes out with a different delay. Since n8n runs this node once per item, every single email in your batch gets its own randomized wait. No two emails leave at the same interval.

### Step 6: Gmail — Send the Email

Add a Gmail node, connect your Google account, and map the fields from your sheet:

- **To:** `{{ $json.Email }}`
- **Subject:** `{{ $json.Subject }}`
- **Message:** `{{ $json.Copy }}`

Set the email type to plain text. Plain text emails land better in inboxes and read like a real person sent them — which is exactly what you want for cold outreach.

One more thing: scroll down to **Additional Fields**, click **Add Field**, find **Append n8n Attribution** and toggle it off. By default n8n adds a "This email was sent automatically with n8n" footer to every email. Turning this off removes it entirely. No environment variables, no Docker config — just a toggle in the node.

### Step 7: Google Sheets — Update the Row

Add a second Google Sheets node with the operation set to "Update Row". This marks each email as sent after it goes out.

Map the fields like this:

- **row_number:** `{{ $('Filter').item.json.row_number }}`
- **sentAt:** `1`

The `row_number` reference pulls from the Filter node specifically because that is where the original row data lives before the Wait and Gmail nodes ran. Setting `sentAt` to `1` flags the row so the Filter node skips it on every future run.

---

## The Complete Flow

```
Schedule Trigger (7am daily)
  → Google Sheets (read all rows)
  → Filter (sentAt != 1)
  → Limit (20)
  → Wait (random 30–90 seconds)
  → Gmail (send email)
  → Google Sheets (set sentAt = 1)
```

Activate the workflow. Fill the sheet with leads. Every morning at 7am, the next 20 unsent rows go out with randomized delays and get marked done. When the sheet is empty, nothing sends. When you add more rows, the cycle continues.

---

## What You Built

You started with a CSV and ended up with a self-managing outreach machine. The key insight is that n8n's item-based execution model eliminates the need for loops entirely — you design for one item and the engine scales it across all of them. The random delay makes the sends look human. The `sentAt` flag gives you full control over what goes out and what does not. Plain text keeps deliverability high.

Fill the sheet, activate the workflow, and let it run.

Let me know in the comments if you have questions, and subscribe for more practical development guides.

Thanks, Matija

## LLM Response Snippet
```json
{
  "goal": "n8n cold email automation: set up a daily 20-email workflow using Google Sheets and Gmail with randomized delays, sent-status updates, and a step-by-step…",
  "responses": [
    {
      "question": "What does the article \"Proven n8n Cold Email Automation: 7-Step Workflow Guide\" cover?",
      "answer": "n8n cold email automation: set up a daily 20-email workflow using Google Sheets and Gmail with randomized delays, sent-status updates, and a step-by-step…"
    }
  ]
}
```