DEV Community

Alex Kane
Alex Kane

Posted on

I automated 80% of my email replies with a free n8n workflow — full JSON inside

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

  1. Gmail Trigger — polls for new unread emails every minute
  2. Skip No-Reply filter — prevents reply loops from automated senders
  3. Intent detector — scans subject + body for keywords: pricing, support, meeting, general
  4. Auto-reply — sends the appropriate template reply
  5. 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";
}
Enter fullscreen mode Exit fullscreen mode

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 }]] }
  }
}
Enter fullscreen mode Exit fullscreen mode

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 everyMinute to everyFiveMinutes to 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)