Order inquiries, quote requests, complaint emails: if your team tracks incoming customer emails in a spreadsheet, someone is doing that work manually. They are opening Gmail, reading the email, typing the sender's name, copying the subject line, logging the date, and moving on. Every time. For every email.

When they are busy, it does not get done. When the sheet is out of date, it is not useful.

This workflow template watches a Gmail label you assign to incoming customer emails, reads each new message, and automatically adds a row to a Google Sheet with the sender, subject, date received, and a snippet of the email body. The email is marked as processed after logging so it is never added twice.

The sheet stays current without anyone having to maintain it.

What You Are Building

A scheduled n8n workflow that:

  1. Polls a Gmail label (e.g. "Customer Inquiries") every 15 minutes
  2. Reads each new email in that label
  3. Appends a new row to your tracking sheet: sender name, sender email, subject, date received, body snippet
  4. Applies a second Gmail label ("Logged") to each processed email so it is not picked up again
  5. Leaves the original email in the inbox; your team still handles replies, they just do not have to update the sheet

Prerequisites

A Gmail label for incoming emails you want to track. Create a label called "Customer Inquiries" (or whatever name fits your use case). Your team applies this label to emails they want tracked, or you can set up a Gmail filter to apply it automatically to emails from customer domains, a specific inbox, or messages with certain subject line keywords.

A Google Sheet set up as an email log. Create a new sheet (or a new tab in an existing one) with these column headers in row 1:

  • Date Received
  • Sender Name
  • Sender Email
  • Subject
  • Body Snippet
  • Logged At

The workflow will append to this sheet. Keep the headers in row 1 and leave the rest of the rows empty: n8n will fill them in.

n8n instance. n8n Cloud or self-hosted, version 1.0 or later.

Gmail credentials in n8n. In Credentials, add a Gmail OAuth2 credential authorized against the mailbox you want to read from.

Google Sheets credentials in n8n. Add a Google Sheets OAuth2 credential authorized against the Google account that owns the log sheet.

Workflow Overview

The complete node sequence:

Schedule Trigger (every 15 minutes)
    → Gmail (Get Messages: label: Customer Inquiries, exclude label: Logged)
    → IF node (check if any new emails were found)
      → True: Loop Over Items
                → Code node (extract and format fields from each email)
                → Google Sheets (Append Row)
                → Gmail Update (add "Logged" label to the email)
      → False: (end, no new emails this cycle)

The "Logged" label exclusion on the Gmail node is the key to avoiding duplicates: once an email has that label, it will never be returned by the trigger again.

Step-by-Step Build

Step 1: Use n8n's AI to Generate the Starting Workflow

Before building any nodes manually, use n8n's built-in AI to generate the workflow structure. This gives you a connected starting point in seconds; you then configure credentials and refine the logic in the steps that follow.

Open n8n and create a new workflow. Click the Generate workflow button in the top-right corner of the canvas (the sparkle/AI icon), or look for the AI description bar that appears on an empty canvas.

Use this prompt:

Build a workflow that runs every 15 minutes. Check Gmail for emails that have the label "Customer Inquiries" but do not have the label "Logged". For each new email, append a row to a Google Sheet with the sender name, sender email address, subject line, date received, and a short snippet of the body. Then add the "Logged" label to each processed email so it is not picked up again on the next run.

The AI will generate a set of connected nodes. It will not be fully configured: credentials are missing, label names are placeholders, and the Code node logic will need to be replaced with the specific JavaScript in the steps below. That is expected.

What the AI typically gets right:

  • The correct node types: Schedule Trigger, Gmail, IF, Loop Over Items, Code, Google Sheets Append, Gmail Update
  • A reasonable connection order
  • Placeholder names that match your prompt

What you will configure in the steps below:

  • Your Gmail and Google Sheets OAuth2 credentials
  • The exact Gmail label names ("Customer Inquiries" and "Logged")
  • The Code node logic to parse sender name and email from Gmail's "from" field
  • The Google Sheet URL and column mapping

Once the AI has generated the workflow, proceed to Step 2 to configure each node.

Step 2: Add the Schedule Trigger

Create a new workflow and add a Schedule Trigger node.

Set it to run every 15 minutes. This keeps the sheet reasonably current throughout the day without hammering the Gmail API. If your inquiry volume is very low, every 30 or 60 minutes is fine.

Step 3: Add the Gmail Get Messages Node

Add a Gmail node after the trigger. Set the operation to Get Many Messages.

Configure it:

  • Credential: Your Gmail OAuth2 credential
  • Filters, Label: Customer Inquiries
  • Filters, Exclude Label: Logged (create this label in Gmail first: even an empty label must exist before you can reference it)
  • Limit: 20 (adjust based on how many emails you expect per 15-minute window: 20 is safe for most teams)

This returns only emails that have the "Customer Inquiries" label but do not yet have the "Logged" label. After logging, we add "Logged" to the email, so it is automatically excluded from the next run.

Run the node manually to confirm it returns emails. Open the output panel to see the Gmail message object fields: you will use from, subject, date, snippet, and id in the next step.

Step 4: Add an IF Node to Handle Empty Results

Add an IF node after the Gmail node. Configure the condition:

  • Value 1: {{ $items().length }}
  • Condition: Greater than
  • Value 2: 0

Connect the True branch to the Loop Over Items node. Leave the False branch unconnected. When no new emails arrive in a 15-minute window, the workflow ends cleanly.

Step 5: Add a Loop Over Items Node

Add a Loop Over Items node on the True branch. This feeds one email at a time to the Extract, Append, and Label nodes. No configuration needed: connect it to receive items from the IF node.

Step 6: Add a Code Node to Extract Email Fields

Add a Code node inside the loop. This node pulls the fields you need from the Gmail message object and formats them cleanly for the sheet.

const email = $json;

// Parse sender name and email from the "from" field
// Gmail returns "from" as "Name <email@domain.com>" or just "email@domain.com"
const fromRaw = email.from || '';
const nameMatch = fromRaw.match(/^(.+?)\s*<(.+?)>$/);
const senderName = nameMatch ? nameMatch[1].trim() : fromRaw.trim();
const senderEmail = nameMatch ? nameMatch[2].trim() : fromRaw.trim();

// Format date
const dateReceived = email.date
  ? new Date(email.date).toISOString().split('T')[0]
  : '';

// Truncate body snippet
const snippet = (email.snippet || '').substring(0, 200);

const loggedAt = new Date().toISOString();

return [{
  json: {
    dateReceived,
    senderName,
    senderEmail,
    subject: email.subject || '(no subject)',
    snippet,
    loggedAt,
    emailId: email.id
  }
}];

Run this node against a test email to confirm all five fields are populated. If senderName and senderEmail are both showing the raw email address (no name parsed), that means Gmail is not including a display name for that sender: the email address appearing in both fields is correct behavior.

Step 7: Add the Google Sheets Append Node

Add a Google Sheets node inside the loop, after the Code node. Set the operation to Append Row.

Configure it:

  • Credential: Your Google Sheets OAuth2 credential
  • Spreadsheet: Your email log sheet URL
  • Sheet: The tab name where you set up the headers
  • Fields to Send: Map each field to its column header:
    • Date Received → {{ $json.dateReceived }}
    • Sender Name → {{ $json.senderName }}
    • Sender Email → {{ $json.senderEmail }}
    • Subject → {{ $json.subject }}
    • Body Snippet → {{ $json.snippet }}
    • Logged At → {{ $json.loggedAt }}

Run the node against a test email and open your Google Sheet to confirm a new row was appended with the correct data.

Step 8: Add the Gmail Update Node to Apply the "Logged" Label

Add a Gmail node after the Google Sheets Append node, still inside the loop. Set the operation to Modify Message.

Configure it:

  • Message ID: {{ $json.emailId }}
  • Add Labels: Logged

This adds the "Logged" label to the email immediately after it is appended to the sheet. On the next workflow run, the Gmail Get Messages node will exclude any email with the "Logged" label, so nothing is logged twice.

Step 9: Activate the Workflow

Toggle the workflow to Active. Test by applying the "Customer Inquiries" label to a real or test email in Gmail, waiting up to 15 minutes, and confirming a new row appears in your Google Sheet and the "Logged" label is applied to the email.

What to Watch Out For

The "Logged" label must exist in Gmail before the workflow runs. If the label does not exist, the Gmail node in Step 2 will either error or silently ignore the exclusion filter. Create both labels ("Customer Inquiries" and "Logged") in Gmail before activating the workflow, even if they are currently empty.

Emails labeled manually versus automatically. If your team is applying the "Customer Inquiries" label by hand, there will be some lag between when an email arrives and when it is logged. If you want real-time logging, set up a Gmail filter (Settings, Filters and Blocked Addresses) to automatically apply the "Customer Inquiries" label to emails from specific domains or addresses. The workflow will pick them up on the next polling interval.

The snippet is not the full email body. Gmail's snippet field returns approximately the first 200 characters of the email body. For most inquiry logging purposes, this is sufficient for triage. If your team needs the full body in the sheet, use email.text instead of email.snippet in the Code node, but truncate it to avoid cells with thousands of characters that slow down the sheet.

How to Extend This Workflow

Add a category or priority column. Inside the Code node, add logic that checks the subject line for keywords ("urgent," "complaint," "RFQ") and sets a "Category" field accordingly. Append that field as an additional column in the sheet, useful for filtering and prioritization without manual tagging.

Route flagged emails to a specific team member. After appending to the sheet, add a Switch node that checks the Category field and sends a Gmail or Slack notification to the relevant person. Sales inquiries go to the sales inbox. Complaints go to the customer service rep. Support questions go to the technical team. No manual forwarding required.

Build a weekly summary report. Once the sheet has accumulated a week of data, add a separate scheduled workflow (runs every Friday at 4pm) that reads the log sheet, counts emails by category, and sends a summary email to your manager. Two workflows: one that logs, one that reports.

Why Start Here

Manual email logging is one of the most durable sources of administrative overhead in small manufacturing operations: it happens every day, requires no judgment, and takes exactly as long as it takes. It is also the kind of task that gets skipped when things are busy, which means the system becomes unreliable at exactly the moments when it is most needed.

This workflow is also a clean foundation for more sophisticated email-based automations. Once you have a reliable log of incoming emails, the next builds (follow-up reminders, categorization, escalation routing) have a structured dataset to work from instead of a raw inbox.

The Flow Kaizen guide covers how to sequence your first five automation builds, starting with quick wins like this one and building toward more connected workflows as your team's confidence grows.