AutomationFlowsEmail & Gmail › Send a Daily Product Newsletter with Google Sheets and Gmail

Send a Daily Product Newsletter with Google Sheets and Gmail

ByRobin @robinvm on n8n.io

This workflow runs daily at 08:00, pulls products scheduled for today from Google Sheets, generates an HTML newsletter email, and sends it to each subscriber via Gmail. Runs every day at 08:00 on a schedule. Reads all rows from the sheet in Google Sheets. Keeps only products…

Cron / scheduled trigger★★★★☆ complexity15 nodesGoogle SheetsGmail
Email & Gmail Trigger: Cron / scheduled Nodes: 15 Complexity: ★★★★☆ Added:

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

This workflow follows the Gmail → 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
{
  "id": "SMzmcQfryP0eGNSK",
  "name": "A (LITE) \u2014 Newsletter: Daily Sender [FREE]",
  "tags": [],
  "nodes": [
    {
      "id": "schedule-lite",
      "name": "Daily at 08:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        400
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * *"
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "config-lite",
      "name": "\u2699\ufe0f Config",
      "type": "n8n-nodes-base.code",
      "position": [
        224,
        400
      ],
      "parameters": {
        "jsCode": "// ========================================\n// CONFIGURATION \u2014 edit only this node\n// ========================================\n// sheetId: Your Google Sheet ID (from the URL between /d/ and /edit)\nconst sheetId = 'YOUR-GOOGLE-SHEET-ID';\n\nreturn [{ json: { sheetId } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "read-products-lite",
      "name": "Read Products",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        448,
        400
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "mode": "name",
          "value": "newsletter_products"
        },
        "documentId": {
          "mode": "id",
          "value": "={{ $('\u2699\ufe0f Config').first().json.sheetId }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "filter-today-lite",
      "name": "Filter by Today",
      "type": "n8n-nodes-base.code",
      "position": [
        672,
        400
      ],
      "parameters": {
        "jsCode": "const today = new Date().toISOString().split('T')[0];\nconst result = [];\nconst allItems = $input.all();\nfor (let i = 0; i < allItems.length; i++) {\n  const item = allItems[i];\n  const raw = item.json.send_date;\n  if (raw) {\n    try {\n      const normalized = new Date(raw).toISOString().split('T')[0];\n      if (normalized === today) {\n        result.push({ json: item.json });\n      }\n    } catch (e) {}\n  }\n}\nreturn result;"
      },
      "typeVersion": 2
    },
    {
      "id": "if-lite",
      "name": "Products Found?",
      "type": "n8n-nodes-base.if",
      "position": [
        880,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond1",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $input.all().length }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "build-html-lite",
      "name": "Build HTML Email",
      "type": "n8n-nodes-base.code",
      "position": [
        1104,
        400
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst today = new Date();\nconst days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\nconst months = ['January','February','March','April','May','June','July','August','September','October','November','December'];\nconst weekday = days[today.getDay()];\nconst dateStr = today.getDate() + ' ' + months[today.getMonth()] + ' ' + today.getFullYear();\nconst products = items.map(i => i.json);\nconst count = products.length;\nconst cards = [];\nfor (let i = 0; i < products.length; i++) {\n  const p = products[i];\n  const hasRef = p.referral_link && p.referral_link.trim() !== '';\n  let card = '<div style=\"margin-bottom:24px;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;\">';\n  if (p.image_url) { card += '<div style=\"padding:12px 12px 0;\"><img src=\"' + p.image_url + '\" style=\"width:100%;max-height:220px;object-fit:contain;display:block;\" alt=\"' + p.product_name + '\" /></div>'; }\n  card += '<div style=\"padding:10px 14px 12px;\">';\n  card += '<h2 style=\"margin:0 0 4px;font-size:15px;color:#111;\">' + p.product_name + '</h2>';\n  card += '<p style=\"margin:0 0 10px;color:#666;line-height:1.4;font-size:13px;\">' + p.description + '</p>';\n  if (hasRef) {\n    card += '<a href=\"' + p.referral_link + '\" style=\"display:inline-block;background:#000;color:#fff;padding:7px 16px;border-radius:6px;text-decoration:none;font-size:13px;\">Learn more \u2192</a>';\n  }\n  card += '</div></div>';\n  cards.push(card);\n}\nconst productCards = cards.join('');\nconst h1text = count + ' new product' + (count > 1 ? 's' : '') + ' for you today';\nconst html = '<!DOCTYPE html><html><head><meta charset=\"utf-8\"></head><body style=\"margin:0;padding:0;background:#f9fafb;\"><div style=\"max-width:600px;margin:0 auto;padding:24px;font-family:-apple-system,sans-serif;\"><h1 style=\"margin:0 0 24px;font-size:24px;color:#111;\">' + h1text + '</h1>' + productCards + '<p style=\"margin-top:32px;font-size:12px;color:#999;\">You are receiving this because you subscribed to our product newsletter.</p></div></body></html>';\nconst subject = count + ' new product' + (count > 1 ? 's' : '') + ' for you \u2013 ' + weekday + ', ' + dateStr;\nreturn [{ json: { html, subject } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "read-recipients-lite",
      "name": "Read Recipients",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1360,
        400
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "mode": "name",
          "value": "newsletter_recipients"
        },
        "documentId": {
          "mode": "id",
          "value": "={{ $('\u2699\ufe0f Config').first().json.sheetId }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "personalize-lite",
      "name": "Prepare per Recipient",
      "type": "n8n-nodes-base.code",
      "position": [
        1552,
        400
      ],
      "parameters": {
        "jsCode": "const template = $('Build HTML Email').first().json;\nconst recipients = $input.all();\nconst result = [];\nfor (let i = 0; i < recipients.length; i++) {\n  const email = recipients[i].json.email;\n  if (!email) continue;\n  result.push({ json: { to: email, subject: template.subject, html: template.html } });\n}\nreturn result;"
      },
      "typeVersion": 2
    },
    {
      "id": "send-lite",
      "name": "Send Newsletter",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1760,
        400
      ],
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "message": "={{ $json.html }}",
        "options": {},
        "subject": "={{ $json.subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "sticky-overview",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -528,
        -16
      ],
      "parameters": {
        "color": 4,
        "width": 430,
        "height": 880,
        "content": "## \ud83d\udcec Daily Product Newsletter\n\n**Who's it for**\nAffiliate marketers, niche-site owners & creators who want an automated daily product newsletter \u2014 without a paid email tool.\n\n**What it does**\nReads products from a Google Sheet and emails a clean HTML newsletter to your subscribers every morning via Gmail.\n\n**How it works**\n1\ufe0f\u20e3 Runs daily at 08:00\n2\ufe0f\u20e3 Reads products, keeps only those due today\n3\ufe0f\u20e3 Builds an HTML email with product cards\n4\ufe0f\u20e3 Sends it to your recipient list\n\n**Setup (~15 min)**\n1. Copy the Google Sheet template \u2192 File \u2192 Make a copy:\nhttps://docs.google.com/spreadsheets/d/1t3x9_qWaklk0T9Z25r4QxUbJ2lqTSU5CjKF_1wwVrgE/edit?usp=sharing\n2. Paste your Sheet ID into the \u2699\ufe0f Config node\n3. Connect Google Sheets + Gmail credentials\n4. Add a product + recipient, test, activate\n\n**Requirements**\nn8n \u00b7 a Google account \u00b7 a Gmail account (all free)\n\n---\n\u2b50 **Want open & click tracking + unsubscribe?**\nThe full version tracks who opens and which products get clicked:\nrvmautomations.lemonsqueezy.com\n\n---\n\n**Want open & click tracking?**\nThis free version sends newsletters but doesn't track opens, clicks or unsubscribes.\n\n\ud83d\udc49 [Upgrade to Premium](https://rvmautomations.lemonsqueezy.com/checkout/buy/83671e3e-481d-4cf4-98ad-eaf22758e379) \u2014 adds full tracking, bot filtering, unsubscribe management and an analytics dashboard. One-time $39."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-sec-a",
      "name": "Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        160
      ],
      "parameters": {
        "color": 5,
        "width": 460,
        "height": 384,
        "content": "## \u23f0 1. Schedule & Config\n\nRuns automatically every day at 08:00 (change it in the trigger).\n\nSet your **Google Sheet ID** in the **\u2699\ufe0f Config** node \u2014 it's the only thing you need to edit."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-sec-b",
      "name": "Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        160
      ],
      "parameters": {
        "color": 5,
        "width": 640,
        "height": 384,
        "content": "## \ud83d\udce5 2. Get today's products\n\nReads all products from the sheet, then keeps only those whose **send_date** is today.\n\nIf no products are due, the workflow stops quietly \u2014 no empty emails."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-sec-c",
      "name": "Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1088,
        160
      ],
      "parameters": {
        "color": 5,
        "width": 230,
        "height": 384,
        "content": "## \u2709\ufe0f 3. Build the email\n\nLoops over today's products and builds a clean HTML newsletter \u2014 each product becomes a card with image, text and an affiliate button."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-sec-d",
      "name": "Section 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1328,
        160
      ],
      "parameters": {
        "color": 5,
        "width": 660,
        "height": 384,
        "content": "## \ud83d\udce4 4. Send to subscribers\n\nReads your recipient list and sends the newsletter to each subscriber via Gmail."
      },
      "typeVersion": 1
    },
    {
      "id": "upgrade-hint-sticky-a1",
      "name": "Upgrade to Premium",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2200,
        160
      ],
      "parameters": {
        "color": 3,
        "width": 420,
        "height": 460,
        "content": "## \u2b50 Upgrade to Premium\n\nThis **free** version sends newsletters but has **no tracking**.\n\nThe **Premium** version adds:\n- \ud83d\udcca Open & click tracking per recipient\n- \ud83e\udd16 Bot filtering (no fake clicks)\n- \ud83d\ude4b Unsubscribe management\n- \ud83d\udcc8 Analytics dashboard (auto-built in Google Sheets)\n\n\ud83d\udc49 [Get the Premium version \u2014 $39 one-time](https://rvmautomations.lemonsqueezy.com/checkout/buy/83671e3e-481d-4cf4-98ad-eaf22758e379)"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "timezone": "Europe/Berlin",
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "e6876c55-d696-4896-a530-75107c2a213e",
  "nodeGroups": [],
  "connections": {
    "Read Products": {
      "main": [
        [
          {
            "node": "Filter by Today",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Config": {
      "main": [
        [
          {
            "node": "Read Products",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily at 08:00": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter by Today": {
      "main": [
        [
          {
            "node": "Products Found?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Products Found?": {
      "main": [
        [
          {
            "node": "Build HTML Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Recipients": {
      "main": [
        [
          {
            "node": "Prepare per Recipient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML Email": {
      "main": [
        [
          {
            "node": "Read Recipients",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare per Recipient": {
      "main": [
        [
          {
            "node": "Send Newsletter",
            "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

This workflow runs daily at 08:00, pulls products scheduled for today from Google Sheets, generates an HTML newsletter email, and sends it to each subscriber via Gmail. Runs every day at 08:00 on a schedule. Reads all rows from the sheet in Google Sheets. Keeps only products…

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

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

YOUR_ID 4. Uses gmail, googleDrive, googleSheets, httpRequest. Scheduled trigger; 53 nodes.

Gmail, Google Drive, Google Sheets +1
Email & Gmail

special-day-email-sender. Uses googleSheets, gmail. Scheduled trigger; 43 nodes.

Google Sheets, Gmail
Email & Gmail

Looking for a way to track GitHub bounty issues automatically and get notified in real time? This GitHub Bounty Tracker workflow monitors repositories for issues labeled 💎 Bounty, logs them in Google

Google Sheets, HTTP Request, WhatsApp +1
Email & Gmail

This workflow automatically sends a beautifully designed HTML newsletter every Sunday at 8 AM, featuring products currently on sale from your Algolia-powered e-commerce store.

Google Sheets, HTTP Request, Gmail
Email & Gmail

This n8n template demonstrates how to build a Auto Lead Gen & Outreach System for Local Businesses specifically designed to help businesses that don’t have a website yet.

Google Sheets, HTTP Request, Google Drive +1