📁 last Posts

n8n Gmail Automation: OAuth2 Setup, AI Auto-Replies & Free Templates

n8n Gmail Automation: OAuth2 Setup, Gmail Trigger, Auto-Reply and Free Templates

⏱️ 18 min read · 📥 2 free importable templates included

Last year I built a client an invoice-reminder automation. It worked for exactly seven days, then died silently — no error, no log, just an inbox that stopped responding. The cause wasn't the workflow. It was a Google Cloud setting with a seven-day fuse that no guide mentioned. That setting is section 2 of this article, and it's the difference between an automation that survives the week and one you rebuild every Monday.

The Direct Answer

Connecting Gmail to n8n takes three steps: (1) create a Google Cloud project and enable the Gmail API; (2) create OAuth credentials with the scopes you actually need, and — critically — publish the app to escape the 7-day token trap; (3) add the credential in n8n and build with the Gmail Trigger (read emails) and the Gmail node (send, reply, draft, label).

The two things that decide whether your automation lives or dies quietly: the OAuth setup in section 3 and the filter queries in section 5. Both get full treatment below, plus two tested, importable templates in section 9 — read the "Before You Activate" checklist first, especially if you're building an auto-responder.

🔗
Disclosure: some links in this guide are affiliate links. If you sign up through them, we may earn a commission at no extra cost to you — it helps keep TriggerWorkflow.com running. We only recommend tools we actually use.

1. What You Can Automate

Gmail is one of the most useful integrations in n8n, because email touches support, sales, invoices, notifications, and internal operations — almost every business process eventually routes through an inbox. The map, in the order you'll actually need it:

AutomationTriggerActionWho needs it
Auto-responderNew email from XReply with a templateEveryone with a busy inbox
Invoice / order alertsEmail with attachmentParse + save + notifyFreelancers, shops
Inbox triageNew emailLabel, star, forward, archiveTeams drowning in mail
Lead captureNew email from your siteAppend to Sheets/CRMMarketers — pairs with our Lead Pipeline guide
Daily unread countScheduleTally unread mail into a messageAnyone checking mail 20×/day
AI email assistantNew emailAgent drafts a reply for approvalSupport teams — section 7

2. The 7-Day Token Trap (Fix This First)

Here's the bug nobody warns you about, and the one that cost my client a silent week: Google Cloud projects default to "Testing" mode, and in Testing mode, Gmail refresh tokens expire after roughly 7 days. No error, no warning — your automation simply stops connecting a week in.

The fix is usually permanent, but read the fine print below before you assume it's bulletproof:

1

Publish the app

Google Cloud Console → APIs & Services → OAuth consent screen → Publishing status → PUBLISH APP. Status changes from "Testing" to "In production".

2

Understand what "no verification needed" actually means

Google will ask about a formal verification process (privacy policy, security review, etc.). For a private app used only by your own Google account or a small list of test users, full verification is typically not required — but this can change if you request sensitive/restricted scopes later or distribute the app publicly. If Google ever forces verification on you, that's a signal your setup grew beyond "personal automation."

3

Reconnect the credential in n8n

Back in n8n, reconnect the credential (Sign in with Google → you'll see an "unverified app" warning → Advanced → Continue). The new refresh token issued after publishing is designed to persist until you revoke it — not 7 days.

⚠️
The order matters: publish first, then reconnect. Tokens issued while the app was in Testing mode keep their short lifespan even after publishing — only fresh tokens issued after publishing behave as long-lived.
🔐
Security note on the "unsafe app" warning: only click through this warning when you are 100% certain the app is your own Google Cloud project — the one you just created and control the client ID/secret for. This screen exists to stop people from authorizing unknown third-party apps; it is not a shortcut to bypass warnings on tools you don't recognize. If a token keeps failing after reconnecting, don't just retry blindly — check the OAuth consent screen status and scopes in Google Cloud Console first.
💡
Prefer zero setup? On n8n Cloud, the Gmail OAuth flow is simplified — no Google Cloud project, no publishing dance. If self-hosting the OAuth puzzle feels like a tax, the cloud plan pays for itself in saved weekends.

3. Google Cloud OAuth Setup (The Part Everyone Gets Wrong)

The full path, in order. Skip a step and you'll meet one of the errors in section 11:

1

Create a project and enable the API

console.cloud.google.com → create a project (any name) → APIs & Services → Library → search "Gmail API" → Enable.

2

Configure the consent screen with the scopes you need

OAuth consent screen → External → fill App name + your email → add scopes based on the table below → add yourself as a test user → save → publish (section 2).

3

Create the OAuth credentials

Credentials → Create credentials → OAuth client ID → Web application. Copy the Client ID and Client Secret — they go into n8n next.

Follow the principle of least privilege — only request the scopes your workflows actually use:

What you want to buildScope to add
Read/trigger on incoming emails onlygmail.readonly
Send, reply, or draft emailsgmail.send
Apply labels, mark as read, archivegmail.modify

Building everything in this guide (trigger + auto-reply + labeling) needs all three. If you only need the daily-digest template, gmail.readonly alone is enough — don't request gmail.modify or gmail.send if nothing in your workflow uses them.


4. Connect n8n (Credential)

In n8n: Credentials → Add credential → Gmail OAuth2. Paste the Client ID and Client Secret. n8n will display an OAuth redirect/callback URL directly inside this credential screen — copy that exact URL into your Google Cloud OAuth client's "Authorized redirect URIs" field. This is the setting that actually controls whether the OAuth handshake succeeds; it is easy to confuse with other URL settings, so here's the difference:

SettingWhat it controlsWhere it lives
WEBHOOK_URLPublic base URL n8n uses to build webhook and OAuth callback linksEnvironment variable on your n8n instance
N8N_EDITOR_BASE_URLURL used for links inside the n8n editor UI itselfEnvironment variable, mostly relevant behind a reverse proxy
OAuth redirect URIThe exact callback n8n sends to Google — must match your Google Cloud client configShown live in the Gmail OAuth2 credential screen in n8n

If you're self-hosting behind a reverse proxy with HTTPS, make sure WEBHOOK_URL reflects your real public HTTPS domain — our Docker guide covers the full reverse-proxy setup.

ℹ️
Localhost testing: OAuth works from localhost too. But the moment you move to a server, update WEBHOOK_URL and re-copy the new redirect URI into Google Cloud, or the redirect dies (the classic localhost error our Webhook Localhost guide fixes).

One credential, two nodes: the Gmail Trigger (incoming) and the Gmail node (outgoing) both select it. Connect once and both work.

5. Reading: The Trigger & Filter Queries

The Gmail Trigger works by polling — it checks your inbox on an interval you configure, it does not push instantly like a webhook. Its most useful setting is the q filter: a Gmail search query that decides which emails even reach your workflow. The operators worth memorizing:

OperatorMatchesExample use
from:[email protected]A specific senderAlert on invoices from your accountant
subject:invoiceKeyword in subjectCatch payment emails
is:unreadOnly unread mailAvoid re-processing on every poll
has:attachmentEmails with filesFile-collection automations
label:updatesInside a labelScope the trigger to one folder
newer_than:1dAge window (approximate)Daily batches — not a guaranteed exact 24h boundary

Combine them like Gmail search: from:[email protected] has:attachment is:unread. The trigger's output is the full email: $json.subject, $json.textPlain (body), $json.id (the message ID — what the Reply operation needs), and binary attachments when you enable download.

🔍
Golden rule before building on fields: run the trigger once with Execute workflow and read the actual JSON. Field shapes vary with the simple option and between accounts/n8n versions — inspecting the real output for thirty seconds beats an hour of guessing why $json.something is empty.
💡
Poll smart, and verify the real delay: every poll costs API quota, and the actual delay between "email arrives" and "workflow runs" depends on your poll interval, your n8n version, and how your instance is deployed. Set your interval, activate the workflow, send yourself a test email, and time it — don't assume it's instant.

6. Sending: Text, HTML, Drafts & Attachments

The Gmail node handles the outgoing side. These four operations cover most workflows you'll build:

  • Send — delivers immediately. Fields: To, Subject, Message.
  • Reply — answers the email that triggered the workflow (needs the email's id).
  • Draft — creates the email in your drafts without sending. The approval-friendly choice: pair it with our Human-in-the-Loop guide and nothing gets sent until a human reads it.
  • Mark as Read / Add Labels — inbox housekeeping after processing, and the key to preventing reply loops (section 8).

Three details that elevate an automation from "works" to "looks professional":

  • HTML emails: choose the HTML type and use basic tags (<b>, <br>, inline styles). Skip complex CSS — Gmail strips it.
  • Attachments: in the node's Options, add attachments from binary data of previous nodes or from URLs.
  • Reply-to: set a different reply address than the sender when the reply should land elsewhere.

7. The AI Email Assistant

The modern upgrade: attach the Gmail tool to an AI Agent, and the agent reads, drafts, and sends as part of its reasoning — you write the policy, the agent writes the mail. Two rules from running this in production:

  • Never send without approval. The agent's Gmail tool belongs behind an approval checkpoint — our HITL guide wires the review step in one minute. An agent that can email customers unsupervised is a PR incident waiting.
  • Give it context, not just instructions. The agent drafts better when it sees the customer's history — memory from our AI Agent Memory guide plus your RAG knowledge base from the RAG tutorial.

The pragmatic setup most people actually need: Trigger → Agent drafts a reply → Gmail node creates a Draft → you review and hit send. Zero risk, full speed.


8. Before You Activate

Any workflow that sends or replies automatically deserves a five-minute safety check before you flip it on:

  • Test with a throwaway account first. Send yourself emails from a second Gmail address and watch the workflow run manually before activating it against real contacts.
  • Never let an auto-responder reply to itself. Exclude your own sending address and generic no-reply@ senders from the trigger query.
  • Mark every processed email. Apply a label (e.g. n8n-processed) right after acting on an email, and exclude that label from future polls — otherwise the same email can be replied to on every single poll cycle.
  • Don't reply to mailing lists or bulk senders. Add exclusions for known newsletter/marketing senders if your inbox receives them.
  • Check your daily send quota (section 10) before pointing an automation at a busy inbox.
⚠️
Skipping the label-based dedup step is the single most common way a Gmail automation embarrasses its owner — either by spamming one sender with the same reply repeatedly, or by triggering a reply loop with another auto-responder. Template 1 in section 9 includes the labeling step built in — don't remove it.

9. Free: 2 Importable Templates

Both templates use real n8n node structures. Import, pick your credential, adjust the filters, test manually, then activate.

Template 1 — Smart Auto-Responder (with loop protection)

Watches for a specific sender and replies with a tailored message. Three design choices make it safer than a basic version: it uses the Reply operation (answers the original message ID directly — no address-parsing to break), the q filter excludes anything already labeled as processed, and a final node applies that label so the same email is never answered twice.

🏷️
Before importing, create a Gmail label named n8n-processed manually in your Gmail account, then use the Gmail node's Label → Get Many operation once to find its ID, and paste that ID into the "Add Label" node below.
{
  "name": "Gmail Smart Auto-Responder",
  "nodes": [
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "filters": {
          "includeSpamTrash": false,
          "includeDrafts": false,
          "q": "from:[email protected] is:unread -label:n8n-processed"
        },
        "options": {}
      },
      "id": "trigger",
      "name": "Gmail Trigger",
      "type": "n8n-nodes-base.gmailTrigger",
      "typeVersion": 1.3,
      "position": [250, 300],
      "credentials": {
        "gmailOAuth2": {
          "id": "YOUR_GMAIL_OAUTH_ID"
        }
      }
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "reply",
        "messageId": "={{ $json.id }}",
        "emailType": "text",
        "message": "Hi,\n\nThanks for your message. I will get back to you within one business day.\n\nBest,\nWilliam",
        "options": {}
      },
      "id": "reply",
      "name": "Reply to Sender",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 1,
      "position": [450, 300],
      "credentials": {
        "gmailOAuth2": {
          "id": "YOUR_GMAIL_OAUTH_ID"
        }
      }
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "addLabels",
        "messageId": "={{ $json.id }}",
        "labelIds": ["YOUR_N8N_PROCESSED_LABEL_ID"]
      },
      "id": "add-label",
      "name": "Mark as Processed",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 1,
      "position": [650, 300],
      "credentials": {
        "gmailOAuth2": {
          "id": "YOUR_GMAIL_OAUTH_ID"
        }
      }
    }
  ],
  "connections": {
    "Gmail Trigger": {
      "main": [
        [
          {
            "node": "Reply to Sender",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reply to Sender": {
      "main": [
        [
          {
            "node": "Mark as Processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true
  }
}

Template 2 — Daily Unread Email Counter (sent to Telegram)

What this actually does: every morning at 8, it counts unread emails from roughly the last day and sends the count as a message to Telegram. It is a counter, not an AI summary — it does not read or summarize email content. If you want real summaries of subject lines and senders, add an AI node between "Get Unread Emails" and "Build Message" (see the note below the code).

{
  "name": "Gmail Daily Unread Counter to Telegram",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * *"
            }
          ]
        }
      },
      "id": "schedule",
      "name": "Every Day 8:00 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [250, 300]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "getAll",
        "returnAll": true,
        "additionalFields": {
          "q": "is:unread newer_than:1d"
        }
      },
      "id": "get-unread",
      "name": "Get Unread Emails",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 1,
      "position": [450, 300],
      "credentials": {
        "gmailOAuth2": {
          "id": "YOUR_GMAIL_OAUTH_ID"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const emails = $input.all();\n\n// This counts unread emails — it does NOT summarize content.\n// To add real summaries, insert an AI node before this step\n// and pass subject/sender fields into the prompt.\nconst count = emails.length;\nconst summary = `📬 Morning check: ${count} unread email${count === 1 ? '' : 's'} from roughly the last 24 hours.`;\n\nreturn [{ json: { count, summary } }];"
      },
      "id": "summarize",
      "name": "Build Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [650, 300]
    },
    {
      "parameters": {
        "chatId": "YOUR_TELEGRAM_CHAT_ID",
        "text": "={{ $json.summary }}"
      },
      "id": "telegram",
      "name": "Telegram Digest",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [850, 300],
      "credentials": {
        "telegramApi": {
          "id": "YOUR_TELEGRAM_CREDENTIAL_ID"
        }
      }
    }
  ],
  "connections": {
    "Every Day 8:00 AM": {
      "main": [
        [
          {
            "node": "Get Unread Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Unread Emails": {
      "main": [
        [
          {
            "node": "Build Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Message": {
      "main": [
        [
          {
            "node": "Telegram Digest",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true
  }
}
  • One credential for both templates — YOUR_GMAIL_OAUTH_ID, the same credential from section 4.
  • Verify your output shape once: run the trigger manually (Execute workflow) and inspect the JSON before wiring downstream nodes — field names vary between the "simple" mode and raw payloads, and between n8n versions.
  • Want a real AI summary instead of a count? Insert an AI node (OpenAI/Anthropic) between "Get Unread Emails" and "Build Message" that receives subjects and senders and returns 2–3 bullet points — be mindful of sending email content to a third-party API if it contains sensitive data.
  • Extend the digest to Gmail instead of Telegram: swap the Telegram node for a Gmail Send node — same structure, one node change.
  • More ready-to-import blueprints live in our n8n Workflow Templates Library.

10. Rate Limits & Quotas

The numbers that decide whether your automation is polite or blocked:

LimitValueWhat it means for you
Daily send quota~100/day (consumer Gmail) · ~500/day (Workspace, plan-dependent)Batch campaigns hit the ceiling fast — pace them
PollingEvery poll costs API quotaPoll every 1–15 min; avoid sub-minute intervals
Batch sendsNo fixed hard limit, but bursts get throttledAdd a Wait node (1–2 s) between sends in loops
Refresh tokenLong-lived after publishing · can expire after months of inactivity or revoked consentPublish (section 2) and reconnect if a credential ever fails
⚠️
Honest advice for campaigns: Gmail is not a bulk-email platform — it's a personal inbox with automation. For newsletters and blasts, use a real sender (Brevo, Mailgun, SES). For everything personal and operational, n8n + Gmail works very well.

11. The 10 Most Common Errors & Fixes

#Error / SymptomCauseFix
1 Credential dies roughly every 7 days, no error App in Testing mode — refresh token has a short lifespan by design Publish to In production, then reconnect once (section 2)
2 invalid_grant / token expired Rotated client secret, long inactivity, or the 7-day trap Reconnect the credential; fix the root cause in section 2
3 Trigger never fires Workflow not activated, or the q filter is too narrow Activate, then test with a wide query (is:unread) first
4 Request had insufficient authentication scopes The action needs a scope you didn't add (send vs readonly) Add the missing scope to the app (section 3), then reconnect
5 Trigger re-processes the same email repeatedly No dedup filter — the email stays unread/unlabeled Add is:unread and a processed-label exclusion (section 9)
6 Auto-responder creates a reply loop with another bot No exclusion for the target's own auto-reply or your own address Exclude no-reply@ and your own sending address in the query (section 8)
7 Failed to send email in loops Hit the daily send quota or burst throttling Pace with a Wait node; check quota (section 10)
8 OAuth redirect fails after moving servers WEBHOOK_URL or the Google Cloud redirect URI still points at the old domain Update WEBHOOK_URL, re-copy the new redirect URI into Google Cloud, then reconnect (section 4)
9 HTML email arrives as raw tags Sent as text type while containing HTML Switch the node to the HTML email type
10 "Google hasn't verified this app" screen Your app is published but unverified — expected for personal use Confirm it's your own app, then Advanced → Continue. No verification needed for personal scope levels
ℹ️
Email automations fail silently more than any other kind. Route their errors through an Error Trigger — the n8n Error Handling guide builds the complete alerting setup, so a dead credential pages you instead of vanishing.

12. Frequently Asked Questions

How do I connect Gmail to n8n?

Create a Google Cloud project, enable the Gmail API, create OAuth credentials, then add a Gmail OAuth2 credential in n8n with the client ID and secret. Connect your account once and both Gmail nodes use the same credential (sections 3–4).

Why does my Gmail connection stop working every 7 days?

Your Google Cloud app is in Testing mode, where refresh tokens have a short lifespan by design. Publish the app to In production, then reconnect the credential once — personal apps typically don't need Google verification, you just accept the unverified-app warning after confirming it's your own app (section 2).

Why is my n8n Gmail trigger not working?

The usual causes: the workflow is not activated, the polling interval is too long, the q filter is too narrow, or the token expired. Test with a wide query (is:unread) first, then tighten (error #3 in section 11).

How do I filter which emails the Gmail Trigger processes?

Use Gmail search operators in the q field: from:[email protected] for a sender, subject:invoice for subjects, is:unread, label:updates, or has:attachment. The trigger only fires for emails matching the query (section 5).

Can n8n send emails with attachments?

Yes. In the Gmail Send operation, open Options and add attachments — they can come from previous nodes as binary data or from URLs (section 6).

Can I use HTML formatting in emails sent from n8n?

Yes. Choose the HTML email type and use HTML tags in the message field. Keep it simple — many email clients strip complex CSS, so basic tags and inline styles are safest (section 6).

What is the difference between sending and drafting an email in n8n?

Send delivers immediately. Draft creates the email in your Gmail drafts folder without sending — perfect for approval flows, where a person reviews and hits send manually (section 6).

What are the Gmail API rate limits I should know?

Sending is capped around 100 emails per day for consumer accounts and about 500 for Google Workspace (varies by plan). Poll every 1–15 minutes for most automations and batch sends with a Wait node between messages (section 10).

How do I stop my Gmail auto-responder from creating a reply loop?

Exclude your own sending address and no-reply@ senders from the trigger query, and apply a processed label to every email right after you reply to it — then exclude that label from future polls with -label:n8n-processed (section 9).

Does an AI agent work with Gmail in n8n?

Yes. Attach the Gmail tool to an AI Agent node and the agent can read, draft, and send emails as part of its reasoning — combine it with an approval checkpoint so the agent never sends without a human review (section 7).


Sources & References

Explore More Guides

William Ayadi - TriggerWorkflow
Ahmed Ayari— TriggerWorkflow The seven-day story in the intro is not a metaphor — it's the invoice-reminder project that taught me Google's Testing mode the hard way, and section 2 exists so you never repeat it. Both templates in section 9 were tested on a live Gmail account with n8n's current stable release, and every error in section 11 is one I've hit, fixed, and written down. Read more guides by Mr.Ayari
Comments