AutomationFlowsEmail & Gmail › Daily Sales Report → Auto-emailed at 8 Am

Daily Sales Report → Auto-emailed at 8 Am

02 - Daily Sales Report → Auto-Emailed at 8 AM. Uses googleSheets, gmail. Scheduled trigger; 4 nodes.

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

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
{
  "name": "02 - Daily Sales Report \u2192 Auto-Emailed at 8 AM",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      },
      "id": "node-schedule",
      "name": "Schedule - 8AM Weekdays",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        260,
        300
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Sales Data",
          "mode": "name"
        },
        "options": {
          "headerRow": 1,
          "returnAllMatches": true
        }
      },
      "id": "node-gsheets-read",
      "name": "Google Sheets - Read Sales Data",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        480,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all();\nconst today = new Date();\nconst yesterday = new Date(today);\nyesterday.setDate(yesterday.getDate() - 1);\nconst dateStr = yesterday.toISOString().split('T')[0];\n\n// Filter to yesterday's data\nconst todayRows = rows.filter(row => {\n  const rowDate = row.json['Date'] || row.json['date'] || '';\n  return rowDate.startsWith(dateStr);\n});\n\nconst allRows = todayRows.length > 0 ? todayRows : rows; // fallback to all if no date match\n\nlet totalRevenue = 0;\nlet totalOrders = 0;\nlet totalUnits = 0;\nconst byRep = {};\nconst byProduct = {};\n\nfor (const row of allRows) {\n  const revenue = parseFloat(row.json['Revenue'] || row.json['Amount'] || 0);\n  const orders = parseInt(row.json['Orders'] || 1, 10);\n  const units = parseInt(row.json['Units'] || 1, 10);\n  const rep = row.json['Sales Rep'] || row.json['rep'] || 'Unknown';\n  const product = row.json['Product'] || row.json['product'] || 'Other';\n\n  totalRevenue += revenue;\n  totalOrders += orders;\n  totalUnits += units;\n\n  byRep[rep] = (byRep[rep] || 0) + revenue;\n  byProduct[product] = (byProduct[product] || 0) + revenue;\n}\n\nconst topReps = Object.entries(byRep)\n  .sort((a, b) => b[1] - a[1])\n  .slice(0, 5)\n  .map(([name, rev]) => `<tr><td style=\"padding:6px 12px;\">${name}</td><td style=\"padding:6px 12px; text-align:right;\">$${rev.toLocaleString('en-US', {minimumFractionDigits:2})}</td></tr>`)\n  .join('');\n\nconst topProducts = Object.entries(byProduct)\n  .sort((a, b) => b[1] - a[1])\n  .slice(0, 5)\n  .map(([name, rev]) => `<tr><td style=\"padding:6px 12px;\">${name}</td><td style=\"padding:6px 12px; text-align:right;\">$${rev.toLocaleString('en-US', {minimumFractionDigits:2})}</td></tr>`)\n  .join('');\n\nconst avgOrderValue = totalOrders > 0 ? (totalRevenue / totalOrders) : 0;\n\nconst reportDate = yesterday.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });\n\nreturn [{\n  json: {\n    reportDate,\n    dateStr,\n    totalRevenue: totalRevenue.toFixed(2),\n    totalOrders,\n    totalUnits,\n    avgOrderValue: avgOrderValue.toFixed(2),\n    topRepsHtml: topReps,\n    topProductsHtml: topProducts,\n    rowCount: allRows.length\n  }\n}];"
      },
      "id": "node-calculate",
      "name": "Code - Calculate Totals & Build Report",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        700,
        300
      ]
    },
    {
      "parameters": {
        "sendTo": "leadership@company.com, cfo@company.com, vpsales@company.com",
        "subject": "\ud83d\udcca Daily Sales Report \u2014 {{ $json.reportDate }}",
        "message": "<html><body style=\"font-family: Arial, sans-serif; max-width: 700px; margin: 0 auto; color: #333;\">\n<div style=\"background: linear-gradient(135deg, #1a1a2e, #4f46e5); padding: 24px; border-radius: 8px 8px 0 0;\">\n  <h1 style=\"color: white; margin: 0; font-size: 24px;\">\ud83d\udcca Daily Sales Report</h1>\n  <p style=\"color: #c7d2fe; margin: 8px 0 0;\">{{ $json.reportDate }}</p>\n</div>\n\n<div style=\"background: #f9fafb; padding: 24px; border-radius: 0 0 8px 8px;\">\n\n  <!-- KPI Cards -->\n  <div style=\"display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 24px;\">\n    <div style=\"background: white; padding: 16px; border-radius: 8px; border-left: 4px solid #4f46e5; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n      <p style=\"margin: 0; color: #6b7280; font-size: 12px; text-transform: uppercase;\">Total Revenue</p>\n      <p style=\"margin: 8px 0 0; font-size: 28px; font-weight: bold; color: #1a1a2e;\">${{ $json.totalRevenue }}</p>\n    </div>\n    <div style=\"background: white; padding: 16px; border-radius: 8px; border-left: 4px solid #10b981; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n      <p style=\"margin: 0; color: #6b7280; font-size: 12px; text-transform: uppercase;\">Total Orders</p>\n      <p style=\"margin: 8px 0 0; font-size: 28px; font-weight: bold; color: #1a1a2e;\">{{ $json.totalOrders }}</p>\n    </div>\n    <div style=\"background: white; padding: 16px; border-radius: 8px; border-left: 4px solid #f59e0b; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n      <p style=\"margin: 0; color: #6b7280; font-size: 12px; text-transform: uppercase;\">Avg Order Value</p>\n      <p style=\"margin: 8px 0 0; font-size: 28px; font-weight: bold; color: #1a1a2e;\">${{ $json.avgOrderValue }}</p>\n    </div>\n  </div>\n\n  <!-- Top Reps Table -->\n  <h3 style=\"color: #1a1a2e; border-bottom: 2px solid #e5e7eb; padding-bottom: 8px;\">\ud83c\udfc6 Top Sales Reps</h3>\n  <table style=\"width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n    <thead><tr style=\"background: #4f46e5; color: white;\">\n      <th style=\"padding: 10px 12px; text-align: left;\">Rep</th>\n      <th style=\"padding: 10px 12px; text-align: right;\">Revenue</th>\n    </tr></thead>\n    <tbody>{{ $json.topRepsHtml }}</tbody>\n  </table>\n\n  <!-- Top Products Table -->\n  <h3 style=\"color: #1a1a2e; border-bottom: 2px solid #e5e7eb; padding-bottom: 8px; margin-top: 24px;\">\ud83d\udce6 Top Products</h3>\n  <table style=\"width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n    <thead><tr style=\"background: #10b981; color: white;\">\n      <th style=\"padding: 10px 12px; text-align: left;\">Product</th>\n      <th style=\"padding: 10px 12px; text-align: right;\">Revenue</th>\n    </tr></thead>\n    <tbody>{{ $json.topProductsHtml }}</tbody>\n  </table>\n\n  <p style=\"margin-top: 24px; color: #6b7280; font-size: 12px;\">Generated automatically by n8n \u2022 Data from {{ $json.rowCount }} transactions \u2022 {{ $json.dateStr }}</p>\n</div>\n</body></html>",
        "options": {}
      },
      "id": "node-gmail-report",
      "name": "Gmail - Email Daily Report",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        920,
        300
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Schedule - 8AM Weekdays": {
      "main": [
        [
          {
            "node": "Google Sheets - Read Sales Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets - Read Sales Data": {
      "main": [
        [
          {
            "node": "Code - Calculate Totals & Build Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Calculate Totals & Build Report": {
      "main": [
        [
          {
            "node": "Gmail - Email Daily Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "reporting"
    },
    {
      "name": "sales"
    },
    {
      "name": "scheduled"
    }
  ],
  "meta": {
    "description": "Every weekday at 8 AM, pulls previous day's sales data from Google Sheets, calculates revenue totals/rankings via Code node, and emails a formatted HTML report to leadership.",
    "prerequisites": [
      "Google Sheets OAuth2 credentials",
      "Gmail OAuth2 credentials",
      "Google Sheet with columns: Date, Revenue, Orders, Units, Sales Rep, Product",
      "Update SPREADSHEET_ID and sheet name in the Sheets node",
      "Update recipient emails in Gmail node"
    ],
    "testingScenario": {
      "happy_path": "Manually trigger workflow; verify email received with correct totals",
      "edge_cases": [
        "Empty sheet \u2192 report shows $0.00",
        "No data for yesterday \u2192 falls back to all rows",
        "Large dataset (1000+ rows) \u2192 check execution timeout",
        "Missing columns \u2192 code handles gracefully with defaults"
      ],
      "sheet_columns": [
        "Date (YYYY-MM-DD)",
        "Revenue",
        "Orders",
        "Units",
        "Sales Rep",
        "Product"
      ]
    }
  }
}

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

02 - Daily Sales Report → Auto-Emailed at 8 AM. Uses googleSheets, gmail. Scheduled trigger; 4 nodes.

Source: https://github.com/satmakuru222/TheAIStackk/blob/main/n8n-workflows/02-daily-sales-report-email.json — 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