I was spending 2+ hours a day responding to the same 4 types of emails: pricing questions, support requests, meeting requests, and general inquiries.
The replies were nearly identical every time. So I built a simple n8n workflow to handle them automatically — and I've been running it for months with zero issues.
Here's how it works, and the full workflow JSON at the end.
What the workflow does
- Gmail Trigger — polls for new unread emails every minute
- Skip No-Reply filter — prevents reply loops from automated senders
- Intent detector — scans subject + body for keywords: pricing, support, meeting, general
- Auto-reply — sends the appropriate template reply
- Google Sheets log — every auto-reply is logged for review
Time to set up: ~15 minutes
n8n version: 1.40+
Cost: Free (no paid nodes)
The intent detection logic
This is the key node — a Code node that does simple keyword matching:
const subject = ($json["subject"] || "").toLowerCase();
const body = ($json["snippet"] || "").toLowerCase();
const text = subject + " " + body;
let intent = "general";
if (text.includes("price") || text.includes("cost") || text.includes("pricing") || text.includes("how much")) {
intent = "pricing";
} else if (text.includes("support") || text.includes("help") || text.includes("issue") || text.includes("problem")) {
intent = "support";
} else if (text.includes("meeting") || text.includes("call") || text.includes("schedule") || text.includes("demo")) {
intent = "meeting";
}
Simple and reliable. No LLM needed for basic intent detection — keyword matching handles 90% of cases correctly.
4 things I learned building this
1. The no-reply filter is critical.
Without it, the workflow replies to automated emails, which often reply back, which you then reply to again — infinite loop. Filter any sender containing "noreply", "no-reply", "mailer-daemon", "bounce".
2. Poll every 5 minutes, not every minute.
Polling every minute works but hammers the Gmail API. Every 5 minutes is effectively the same experience for email — nobody notices a 4-minute delay in an auto-reply.
3. Always log to a spreadsheet.
The log is how you catch edge cases. After a week, review what got auto-replied and tune your keyword lists. I added "pricing inquiry" and "price check" after seeing them in the logs.
4. Test with a throwaway Gmail account first.
I cannot stress this enough. Point it at a throwaway first, send test emails with different subjects, verify the right replies go out. Takes 10 minutes and saves you from accidentally spam-replying your entire inbox.
Full workflow JSON
Import this into n8n: Workflows → New → ⋮ → Import from JSON → paste.
{
"name": "Email Auto-Responder",
"nodes": [
{
"parameters": {
"pollTimes": { "item": [{ "mode": "everyMinute" }] },
"filters": {}
},
"id": "e1",
"name": "Gmail Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"typeVersion": 1,
"position": [240, 300]
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json[\"from\"] }}",
"operation": "notContains",
"value2": "noreply"
}
]
}
},
"id": "e2",
"name": "Skip No-Reply",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [460, 300]
},
{
"parameters": {
"jsCode": "const subject = ($json[\"subject\"] || \"\").toLowerCase();\nconst body = ($json[\"snippet\"] || \"\").toLowerCase();\nconst text = subject + \" \" + body;\n\nlet intent = \"general\";\nif (text.includes(\"price\") || text.includes(\"cost\") || text.includes(\"pricing\")) {\n intent = \"pricing\";\n} else if (text.includes(\"support\") || text.includes(\"help\") || text.includes(\"issue\")) {\n intent = \"support\";\n} else if (text.includes(\"meeting\") || text.includes(\"call\") || text.includes(\"schedule\")) {\n intent = \"meeting\";\n}\n\nconst replies = {\n pricing: \"Hi,\\n\\nThanks for reaching out! Here is our current pricing:\\n\\n[PASTE YOUR PRICING HERE]\\n\\nBest,\\nAlex\",\n support: \"Hi,\\n\\nThanks for contacting us. We will respond within 24 hours.\\n\\nBest,\\nAlex\",\n meeting: \"Hi,\\n\\nYou can book a call here: [YOUR CALENDLY LINK]\\n\\nBest,\\nAlex\",\n general: \"Hi,\\n\\nThanks for your message! We will get back to you within 24 hours.\\n\\nBest,\\nAlex\"\n};\n\nreturn [{ json: { ...items[0].json, intent, replyText: replies[intent] } }];"
},
"id": "e3",
"name": "Detect Intent",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [680, 300]
},
{
"parameters": {
"sendTo": "={{ $json[\"from\"] }}",
"subject": "Re: {{ $json[\"subject\"] }}",
"message": "={{ $json[\"replyText\"] }}",
"options": {}
},
"id": "e4",
"name": "Send Reply",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [900, 300]
}
],
"connections": {
"Gmail Trigger": { "main": [[{ "node": "Skip No-Reply", "type": "main", "index": 0 }]] },
"Skip No-Reply": { "main": [[{ "node": "Detect Intent", "type": "main", "index": 0 }]] },
"Detect Intent": { "main": [[{ "node": "Send Reply", "type": "main", "index": 0 }]] }
}
}
What to customize
- The reply templates for each intent (fill in your actual pricing, FAQ URL, calendar link)
- Add more intent keywords for your specific inbox patterns
- Change
everyMinutetoeveryFiveMinutesto reduce API calls
Want the extended version?
The free workflow above handles the basics. The paid version (part of a template pack I've been building) adds:
- AI-powered intent detection using OpenAI for edge cases keyword matching misses
- Attachment handling — flags emails with attachments for human review
- Priority routing — escalates emails from VIP senders to Slack immediately
- Unsubscribe handling — detects "unsubscribe" requests and logs them without replying
- Multi-inbox support — handles multiple Gmail accounts in one workflow
Full pack: https://stripeai.gumroad.com — 15 n8n workflows, all with setup guides.
Happy to answer questions on the free version in the comments.
Top comments (0)