AutomationFlowsAI & RAG › Leadinboxtriagebot Gt

Leadinboxtriagebot Gt

LeadInboxTriageBot_GT. Uses gmailTrigger, openAi, googleSheets, gmail. Event-driven trigger; 36 nodes.

Event trigger★★★★★ complexityAI-powered36 nodesGmail TriggerOpenAIGoogle SheetsGmailSlack
AI & RAG Trigger: Event Nodes: 36 Complexity: ★★★★★ AI nodes: yes Added:
Leadinboxtriagebot Gt — n8n workflow card showing Gmail Trigger, OpenAI, Google Sheets integration

This workflow follows the Gmail → Gmail Trigger 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": "LeadInboxTriageBot_GT",
  "nodes": [
    {
      "parameters": {
        "content": "# Phase 3 \u00b7 Routing & CRM Logging\n## Lead Inbox Triage Bot \u2014 Grootan Internal Training (batch `batch-may-2026`)\n\n**Trainee:** `<initials = GT>`\n\n**This phase builds:** the Switch (4 outputs + fallback), per-tab Lookup \u2192 IF idempotency check, explicit-column Sheets appends, `duplicate_skipped=true` flag on the skip path, and fallback routing to the `errors` tab so misfires never disappear silently.\n\n_See `docs/PHASES.md` for the acceptance criteria for this phase._",
        "height": 160,
        "width": 1180,
        "color": 7
      },
      "id": "11a62393-8cb6-4021-bac3-70743a06c5d4",
      "name": "Section_PhaseBanner",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        40,
        40
      ]
    },
    {
      "parameters": {
        "content": "# \ud83d\udfe1 Trigger",
        "height": 160,
        "width": 200,
        "color": 5
      },
      "id": "925458bf-3239-41e3-82c5-15279d881e83",
      "name": "Section_Trigger",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        400,
        220
      ]
    },
    {
      "parameters": {
        "content": "# \ud83d\udfe0 Pre-process",
        "height": 160,
        "width": 200,
        "color": 4
      },
      "id": "ba82c64e-05e7-48be-9de1-253c9fb2cf46",
      "name": "Section_Preprocess",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1000,
        220
      ]
    },
    {
      "parameters": {
        "content": "# \ud83d\udfe6 Classify",
        "height": 160,
        "width": 200,
        "color": 6
      },
      "id": "e7cf237a-291e-4be7-a92c-3cdd7cc6d1f2",
      "name": "Section_Classify",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1700,
        220
      ]
    },
    {
      "parameters": {
        "content": "# \ud83d\udfe2 Route\nSwitch by category. Fallback \u2192 'Log unknown category to errors'.",
        "height": 160,
        "width": 320,
        "color": 2
      },
      "id": "c86159dd-ff17-494e-b9d5-6676205ce6f3",
      "name": "Section_Route",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2300,
        220
      ]
    },
    {
      "parameters": {
        "content": "# \ud83d\udd34 Log\nLookup \u2192 IF \u2192 Append per category, with explicit columns.\nDuplicate path emits a `duplicate_skipped=true` Set marker (PDF \u00a73.4).",
        "height": 140,
        "width": 620,
        "color": 3
      },
      "id": "6248a5c7-7679-4222-9afb-a32b5ddfc19c",
      "name": "Section_Log",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2700,
        220
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "37af5e80-72bb-45b5-b057-6042dc3c017e",
              "name": "spreadsheetId",
              "type": "string",
              "value": "REPLACE_WITH_YOUR_GOOGLE_SHEET_ID"
            },
            {
              "id": "b8e08c69-9312-49f1-a3c4-ec66bf17f77f",
              "name": "slackChannel",
              "type": "string",
              "value": "#training-leads"
            },
            {
              "id": "ae107414-77de-41d6-8719-bd18ee5e4387",
              "name": "slackErrorChannel",
              "type": "string",
              "value": "#training-leads-errors"
            },
            {
              "id": "484f7b63-258c-41c2-939c-433fa624fff8",
              "name": "model",
              "type": "string",
              "value": "gpt-4o-mini"
            },
            {
              "id": "dea5a4b0-7738-44cb-a31d-1f1c0a4cdc67",
              "name": "temperature",
              "type": "number",
              "value": 0.1
            },
            {
              "id": "32559494-29a5-4a18-8528-a85210a7fd12",
              "name": "signature",
              "type": "string",
              "value": "\u2014 Grootan Team\n+91-44-XXXX-XXXX | grootan.com"
            },
            {
              "id": "6d7078d2-436a-4aa6-b3fe-0b7213943637",
              "name": "batch",
              "type": "string",
              "value": "batch-may-2026"
            }
          ]
        },
        "options": {}
      },
      "id": "2d40f1f0-f4ee-426e-a7c6-c457f1281388",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [
        400,
        460
      ],
      "notes": "Tenant-style settings. Point spreadsheetId / Slack channels / model / signature here. Prompts live in the separate 'Prompts' Set node.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "simple": false,
        "filters": {
          "labelIds": [
            "INBOX"
          ],
          "readStatus": "unread"
        },
        "options": {
          "downloadAttachments": false,
          "markAsRead": true
        }
      },
      "id": "78b7f986-d0b4-42cc-b6ad-d049d72ed832",
      "name": "Watch inbox",
      "type": "n8n-nodes-base.gmailTrigger",
      "typeVersion": 1,
      "position": [
        400,
        280
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "notes": "Polls INBOX every 1 minute. options.markAsRead=true so the same email is not re-processed on retry. continueOnFail=false; Gmail Triggers do not support n8n-level retries (see RUNBOOK \u00a73).",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "0c8821cb-e2e5-4419-b277-8e485c4267b9",
              "name": "classification_system_prompt",
              "type": "string",
              "value": "You are a strict, deterministic email triage assistant for Grootan Technologies'\nshared sales inbox. Your only job is to classify ONE incoming email into exactly\none of four categories and return a JSON object \u2014 nothing else, no prose, no\nmarkdown fences, no commentary.\n\n## Categories\n- LEAD     \u2014 a prospective customer (or someone on their behalf) asking about\n            Grootan's services, requesting a quote/RFP, asking for a demo, or\n            otherwise initiating a sales conversation. The sender is NOT already\n            an active customer asking for help with an existing product.\n- SUPPORT  \u2014 an existing customer (or someone using one of Grootan's products)\n            reporting a bug, asking a how-to question, or asking for help with\n            something they already use.\n- SPAM     \u2014 unsolicited marketing, newsletters, cold outreach with no clear\n            relationship to Grootan's offering, SEO link-building, list-bombing,\n            \"I can rank your site\" / \"I can build your app for $5/hr\" pitches,\n            promotional discounts, generic mass campaigns.\n- OTHER    \u2014 internal forwards, automated notifications (calendar invites,\n            receipts, system alerts), bounces, anything that doesn't fit the\n            three categories above.\n\n## Output schema (return EXACTLY this shape)\n{\n  \"category\":           \"LEAD\" | \"SUPPORT\" | \"SPAM\" | \"OTHER\",\n  \"confidence\":         <number between 0.0 and 1.0>,\n  \"reasoning\":          \"<one sentence, \u2264 240 chars, explaining the call>\",\n  \"suggested_priority\": \"LOW\" | \"MEDIUM\" | \"HIGH\"   // ONLY when category=LEAD; null otherwise\n}\n\n## Priority rubric (LEAD only)\n- HIGH   \u2014 explicit budget/timeline, named decision-maker title, or named\n           competitor evaluation. Examples: \"we need this rolled out by Q3\",\n           \"I'm the CTO and we're choosing between X and you\", \"RFP attached\".\n- MEDIUM \u2014 clear interest with some specifics (team size, use case) but no\n           hard timeline or budget.\n- LOW    \u2014 vague interest (\"interested in your services\") with no specifics.\n\n## Hard rules\n1. Output MUST be valid JSON parseable by JSON.parse. No code fences. No\n   leading or trailing text. No keys other than the four above.\n2. confidence is a float between 0 and 1. If you would say \"I'm not sure\",\n   use 0.55\u20130.7. Reserve 0.9+ for unambiguous cases.\n3. If sender_domain is a free webmail (gmail.com, outlook.com, yahoo.com) AND\n   the email lacks specifics, this is rarely a real LEAD \u2014 bias toward LOW\n   priority or OTHER.\n4. Newsletters with \"view in browser\", \"unsubscribe\", or \"list-unsubscribe\"\n   markers are SPAM, not LEAD, even if the topic is relevant.\n5. A reply chain pasted in the body does NOT change the classification \u2014 judge\n   only the most recent message (the cleaned body you receive will already\n   have quotes stripped).\n\n## Few-shot examples\n\n### Example 1 \u2014 LEAD (HIGH)\nSubject: Quote for Q3 rollout \u2014 200 ops team\nBody:    Hi Grootan, we're evaluating workflow vendors for our 200-person ops\n         team. Need automation for ticket triage, ETA decision by July. Can we\n         schedule a 30-min call this week? CTO will join.\nOutput:\n{\"category\":\"LEAD\",\"confidence\":0.95,\"reasoning\":\"Named decision-maker, team size, hard timeline, asks for call.\",\"suggested_priority\":\"HIGH\"}\n\n### Example 2 \u2014 SPAM\nSubject: \ud83d\ude80 Boost your rankings \u2014 50% OFF this week!\nBody:    Hi there, our SEO agency can rank your site #1 on Google in 30 days.\n         100% guaranteed. Click here for a free audit. Unsubscribe at the\n         bottom.\nOutput:\n{\"category\":\"SPAM\",\"confidence\":0.99,\"reasoning\":\"Generic SEO pitch with discount hook and unsubscribe footer.\",\"suggested_priority\":null}"
            },
            {
              "id": "5dd110c2-12e4-4941-810b-5ce03a375ad3",
              "name": "reply_system_prompt",
              "type": "string",
              "value": "You are drafting a reply from a Grootan Technologies sales rep to an inbound\nprospect. Your reply will be saved as a Gmail DRAFT for a human to review and\nedit \u2014 it will NOT be sent automatically. Optimise for tone and accuracy; the\nhuman is your safety net for facts but not for tone.\n\n## Hard constraints\n- Length: 80\u2013150 words. Count words; do not exceed 150 or fall below 80.\n- Address the sender by first name only. If only an email is available, use a\n  warm but neutral opener (\"Hi there,\") rather than guessing a name.\n- Match Grootan's tone: warm, professional, concise. No exclamation marks,\n  no superlatives (\"absolutely thrilled\", \"amazing\"), no emoji.\n- Do NOT quote pricing, discounts, or specific SLAs. If the prospect asks for\n  pricing, say a rep will share an indicative range on the call.\n- Do NOT invent facts about the sender's company, headcount, tech stack, or\n  industry beyond what the email itself states. If the email is thin on\n  context, ASK 1\u20132 clarifying questions instead of fabricating.\n- Always propose a concrete next step: a 20-minute discovery call. Ask for\n  2\u20133 time-slot options from the sender (do not propose specific times \u2014 we\n  do not have their calendar).\n- End with the exact signature block provided in the user message under\n  \"signature\". Do not modify the signature.\n\n## Output format\nPlain text. No subject line (the workflow sets it). No greeting decorations.\nNo closing flourish before the signature. Just: greeting \u2192 body \u2192 call to\naction \u2192 signature.\n\n## Few-shot \u2014 thin context\nUser input:\n{\n  \"name\": \"Alex\",\n  \"domain\": \"gmail.com\",\n  \"priority\": \"LOW\",\n  \"body\": \"Hi, interested in your services. Pls share details.\",\n  \"signature\": \"\u2014 Grootan Team\\n+91-44-XXXX-XXXX | grootan.com\"\n}\nOutput:\nHi Alex,\n\nThanks for reaching out \u2014 happy to share more about what we do. To make the\nnext conversation useful, could you tell me a bit about what you're trying\nto solve and the size of the team involved? That way I can point to the\nmost relevant case studies rather than sending a generic overview.\n\nWould a 20-minute discovery call work? If yes, please send 2\u20133 time slots\nthat suit you and I'll lock one in.\n\n\u2014 Grootan Team\n+91-44-XXXX-XXXX | grootan.com\n\n## Few-shot \u2014 rich context\nUser input:\n{\n  \"name\": \"Priya\",\n  \"domain\": \"acme.com\",\n  \"priority\": \"HIGH\",\n  \"body\": \"We're a 200-person ops team evaluating workflow vendors. Need ticket triage automation by Q3. CTO will join the call.\",\n  \"signature\": \"\u2014 Grootan Team\\n+91-44-XXXX-XXXX | grootan.com\"\n}\nOutput:\nHi Priya,\n\nThanks for the detail \u2014 a 200-person ops team with a Q3 target gives us a\nclear starting point. We've shipped ticket-triage automations for teams of\nsimilar size and can walk through the rough architecture, integration\npoints with your existing tools, and a realistic rollout plan on the call.\n\nA 20-minute discovery slot with your CTO works well for kickoff. Could you\nshare 2\u20133 time slots over the next week and I'll confirm one back?\n\n\u2014 Grootan Team\n+91-44-XXXX-XXXX | grootan.com"
            }
          ]
        },
        "options": {}
      },
      "id": "607481a3-4ca8-4d70-b7d8-9148bd8aee8e",
      "name": "Prompts",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [
        700,
        580
      ],
      "notes": "Centralized prompts. Edit here to change classifier or reply behaviour without opening the AI nodes.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "0c361cdc-a153-4c7a-be03-ab8f12a0626a",
              "name": "message_id",
              "type": "string",
              "value": "={{ $json.id || $json.messageId }}"
            },
            {
              "id": "8e5bd93a-ff36-4a94-b993-6df59c863119",
              "name": "thread_id",
              "type": "string",
              "value": "={{ $json.threadId }}"
            },
            {
              "id": "24ca1560-43ae-414a-90b0-f6aaca917dde",
              "name": "received_at",
              "type": "string",
              "value": "={{ $json.internalDate ? new Date(parseInt($json.internalDate)).toISOString() : ($json.headers?.date ? new Date($json.headers.date).toISOString() : $now.toISO()) }}"
            },
            {
              "id": "e62a8eb7-4ef0-41d3-beb1-033d6ef15538",
              "name": "from_raw",
              "type": "string",
              "value": "={{ $json.from?.value?.[0]?.address ? `${$json.from.value[0].name || ''} <${$json.from.value[0].address}>` : ($json.from || $json.headers?.from || '') }}"
            },
            {
              "id": "23dea5a9-15f1-49e5-8e7d-0f7f096ad752",
              "name": "sender_email",
              "type": "string",
              "value": "={{ (($json.from?.value?.[0]?.address) || (($json.from || $json.headers?.from || '').match(/<([^>]+)>/)?.[1]) || ($json.from || '').trim()).toLowerCase() }}"
            },
            {
              "id": "9f40dee5-2c51-4fbd-8958-997c7fd5ef19",
              "name": "sender_name",
              "type": "string",
              "value": "={{ $json.from?.value?.[0]?.name?.trim() || (($json.from || $json.headers?.from || '').match(/^(.*?)\\s*<[^>]+>$/)?.[1]?.replace(/['\"]/g,'').trim()) || (($json.from || '').split('@')[0]) }}"
            },
            {
              "id": "be72158b-334f-40d3-a4e8-b6b6c9563f1b",
              "name": "sender_domain",
              "type": "string",
              "value": "={{ (($json.from?.value?.[0]?.address) || (($json.from || $json.headers?.from || '').match(/<([^>]+)>/)?.[1]) || ($json.from || '')).split('@')[1]?.toLowerCase()?.trim() }}"
            },
            {
              "id": "4bd544c3-bbcf-4e18-804d-ded8a02d227d",
              "name": "subject_clean",
              "type": "string",
              "value": "={{ ($json.subject || '').replace(/^\\s*(Re:|RE:|Fwd:|FW:|Fw:)\\s*/gi, '').trim() }}"
            },
            {
              "id": "df816eba-c820-4753-89c9-f7c517dfc9fa",
              "name": "body_clean",
              "type": "string",
              "value": "={{ (($json.text || $json.body || $json.snippet || '')\n  .replace(/On\\s+.+?wrote:[\\s\\S]*$/m, '')\n  .replace(/-----Original Message-----[\\s\\S]*$/m, '')\n  .replace(/\\n--\\s*\\n[\\s\\S]*$/m, '')\n  .replace(/\\n_{2,}\\n[\\s\\S]*$/m, '')\n  .trim()) }}"
            }
          ]
        },
        "options": {
          "include": "all"
        }
      },
      "id": "0954e640-3e55-46ad-bce8-e8b2c8d26ff7",
      "name": "Clean email",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [
        1000,
        320
      ],
      "notes": "Strips Re:/Fwd: from subject, removes quoted replies, RFC 3676 signatures, and extracts sender_name / sender_email / sender_domain.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "64c9a113-bf8d-4ab5-b74c-1337d2ed7d60",
              "name": "body_preview",
              "type": "string",
              "value": "={{ ($json.body_clean || '').slice(0, 500) }}"
            }
          ]
        },
        "options": {
          "include": "all"
        }
      },
      "id": "7954ff61-ff39-4702-acbd-bf9012f01f36",
      "name": "Compute preview",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [
        1300,
        320
      ],
      "notes": "Two-pass body_preview: takes the first 500 chars of body_clean from the previous Set node. Fixes the bug where preview leaked quoted replies and signatures.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "text",
        "operation": "message",
        "modelId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.model }}"
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "={{ $node[\"Prompts\"].json.classification_system_prompt }}"
            },
            {
              "role": "user",
              "content": "=Subject: {{ ($json.subject_clean || '').slice(0, 200) }}\n\nFrom: {{ ($json.sender_email || '').slice(0, 120) }} (domain: {{ ($json.sender_domain || '').slice(0, 120) }})\n\nBody:\n{{ ($json.body_clean || '').slice(0, 2000) }}"
            }
          ]
        },
        "jsonOutput": true,
        "options": {
          "temperature": "={{ $node[\"Config\"].json.temperature }}",
          "responseFormat": "json_object"
        }
      },
      "id": "105e720a-6599-4393-973c-f51f49ff42bd",
      "name": "Classify email",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.6,
      "position": [
        1700,
        320
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "notes": "JSON mode, temperature 0.1, model from Config, prompt from the separate 'Prompts' Set node.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "functionCode": "// Safety net: NEVER throw. Always emit a valid {category, confidence, reasoning,\n// suggested_priority, parse_failed} object so downstream Switch never crashes.\nconst ALLOWED = ['LEAD','SUPPORT','SPAM','OTHER'];\nconst PRIOS   = ['LOW','MEDIUM','HIGH'];\n\nreturn items.map(item => {\n  const upstream = item.json;\n  let raw = upstream.message?.content\n         ?? upstream.content\n         ?? upstream.text\n         ?? upstream.output\n         ?? upstream.choices?.[0]?.message?.content\n         ?? '';\n  if (typeof raw !== 'string') raw = JSON.stringify(raw);\n\n  let parsed = {};\n  try { parsed = JSON.parse(raw); } catch { /* leave parsed = {} */ }\n\n  const category = ALLOWED.includes(parsed.category) ? parsed.category : 'OTHER';\n  let confidence = Number(parsed.confidence);\n  if (!Number.isFinite(confidence)) confidence = 0.0;\n  confidence = Math.max(0, Math.min(1, confidence));\n  const reasoning = String(parsed.reasoning || '').slice(0, 280);\n  let suggested_priority = null;\n  if (category === 'LEAD') {\n    suggested_priority = PRIOS.includes(parsed.suggested_priority)\n      ? parsed.suggested_priority\n      : 'MEDIUM';\n  }\n  const parse_failed = !raw || !parsed.category;\n\n  // Carry the already-cleaned email fields forward. We pull from 'Compute preview'\n  // (the most-downstream pre-processing node) so body_preview is included.\n  const carry = $('Compute preview').item?.json || $('Clean email').item?.json || {};\n\n  return {\n    json: {\n      ...carry,\n      category,\n      confidence,\n      reasoning,\n      suggested_priority,\n      parse_failed,\n      raw_ai_output: raw.slice(0, 1000),\n    }\n  };\n});\n"
      },
      "id": "3841c61b-9390-4f99-b6f7-cdb446e6b1af",
      "name": "Parse classification",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        2000,
        320
      ],
      "notes": "Safety net \u2014 guarantees one of LEAD/SUPPORT/SPAM/OTHER on 100% of inputs. Carries cleaned fields forward incl. body_preview.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "id": "f097662d-ca97-407f-86a1-70e10776d887",
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "LEAD",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "LEAD"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "id": "0e55f604-b61a-479e-b7e9-72b841ea06fa",
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "SUPPORT",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "SUPPORT"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "id": "613081c2-b127-4bc1-8c4f-9822590d6ca3",
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "SPAM",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "SPAM"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "id": "a6eb9460-9d1f-40a8-a006-2a3a10a08b59",
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "OTHER",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "OTHER"
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "Unknown"
        }
      },
      "id": "593807f3-08bf-4c8a-94bf-eae1bdcf0ebf",
      "name": "Route by category",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        2300,
        320
      ],
      "notes": "Routes on $json.category. Fallback output (index 4) routes to the 'Log unknown category' path that writes the errors tab \u2014 per PDF \u00a73.2.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.spreadsheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "leads",
          "cachedResultName": "leads"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "message_id",
              "lookupValue": "={{ $json.message_id }}"
            }
          ]
        },
        "options": {
          "returnFirstMatch": true
        }
      },
      "id": "6cf6755c-a9c1-463e-b03e-2c18284f6b8d",
      "name": "Lookup leads",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        2700,
        -180
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "continueOnFail": true,
      "notes": "Reads existing message_ids from 'leads' so we don't insert duplicates.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "37cb0eea-d428-475a-b6d1-258479a126b5",
              "leftValue": "={{ $json.message_id ? $json.message_id : '' }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "5285b7f4-cfc6-4530-a200-dd93d459e84a",
      "name": "Already in leads?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2960,
        -180
      ],
      "notes": "Output 0 (TRUE) = duplicate, SKIP append.\nOutput 1 (FALSE) = new message, proceed to append.\nCondition: $json.message_id is non-empty (lookup returned a row).",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "99d90b7a-9d27-46e5-a583-38e793224a9a",
              "name": "duplicate_skipped",
              "type": "boolean",
              "value": true
            },
            {
              "id": "078c4686-3520-41c3-b3e6-9795abb68a01",
              "name": "duplicate_tab",
              "type": "string",
              "value": "leads"
            },
            {
              "id": "701dfae4-0851-4d9a-81b1-3254edfe9a6b",
              "name": "message_id",
              "type": "string",
              "value": "={{ $json.message_id }}"
            }
          ]
        },
        "options": {
          "include": "all"
        }
      },
      "id": "af184dd9-b5cb-42ab-8e2a-92e30a594f40",
      "name": "Duplicate skipped (leads)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [
        3200,
        -260
      ],
      "notes": "PDF \u00a73.4: short-circuit with duplicate_skipped=true for leads.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.spreadsheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "leads",
          "cachedResultName": "leads"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "message_id": "={{ $('Parse classification').item.json.message_id }}",
            "received_at": "={{ $('Parse classification').item.json.received_at }}",
            "sender_email": "={{ $('Parse classification').item.json.sender_email }}",
            "sender_domain": "={{ $('Parse classification').item.json.sender_domain }}",
            "subject": "={{ $('Parse classification').item.json.subject_clean }}",
            "body_preview": "={{ $('Parse classification').item.json.body_preview }}",
            "priority": "={{ $('Parse classification').item.json.suggested_priority }}",
            "confidence": "={{ $('Parse classification').item.json.confidence }}",
            "status": "NEW",
            "created_at": "={{ $now.toISO() }}"
          },
          "matchingColumns": []
        },
        "options": {}
      },
      "id": "9b2863f1-a814-4870-9920-5268e0c85421",
      "name": "Append leads",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        3200,
        -100
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "notes": "Explicit column mapping per sheets/CRM_SHEET_TEMPLATE.md.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.spreadsheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "support",
          "cachedResultName": "support"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "message_id",
              "lookupValue": "={{ $json.message_id }}"
            }
          ]
        },
        "options": {
          "returnFirstMatch": true
        }
      },
      "id": "ddf45756-ecdb-4d04-a694-9306a6a1a818",
      "name": "Lookup support",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        2700,
        40
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "continueOnFail": true,
      "notes": "Reads existing message_ids from 'support' so we don't insert duplicates.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "f8a20972-b70a-4ab6-b158-c87063b6f185",
              "leftValue": "={{ $json.message_id ? $json.message_id : '' }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "6c01636f-42c8-41b8-bd2f-c41d580e6729",
      "name": "Already in support?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2960,
        40
      ],
      "notes": "Output 0 (TRUE) = duplicate, SKIP append.\nOutput 1 (FALSE) = new message, proceed to append.\nCondition: $json.message_id is non-empty (lookup returned a row).",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f0463e4c-fe4f-4b0e-8f5a-2f49a4eb87a9",
              "name": "duplicate_skipped",
              "type": "boolean",
              "value": true
            },
            {
              "id": "50b9a5b6-b62b-4f6d-87b0-2e41d45afb40",
              "name": "duplicate_tab",
              "type": "string",
              "value": "support"
            },
            {
              "id": "d7b2def8-1de4-4950-86ad-2015bd2b898a",
              "name": "message_id",
              "type": "string",
              "value": "={{ $json.message_id }}"
            }
          ]
        },
        "options": {
          "include": "all"
        }
      },
      "id": "d1132226-8d09-4bf7-b76f-4d0937edaa0a",
      "name": "Duplicate skipped (support)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [
        3200,
        -40
      ],
      "notes": "PDF \u00a73.4: short-circuit with duplicate_skipped=true for support.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.spreadsheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "support",
          "cachedResultName": "support"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "message_id": "={{ $('Parse classification').item.json.message_id }}",
            "received_at": "={{ $('Parse classification').item.json.received_at }}",
            "sender_email": "={{ $('Parse classification').item.json.sender_email }}",
            "subject": "={{ $('Parse classification').item.json.subject_clean }}",
            "body_preview": "={{ $('Parse classification').item.json.body_preview }}",
            "confidence": "={{ $('Parse classification').item.json.confidence }}",
            "created_at": "={{ $now.toISO() }}"
          },
          "matchingColumns": []
        },
        "options": {}
      },
      "id": "d2ef6610-242d-4fae-bb63-528147268c8c",
      "name": "Append support",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        3200,
        120
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "notes": "Append to 'support' tab with explicit column mapping.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.spreadsheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "spam",
          "cachedResultName": "spam"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "message_id",
              "lookupValue": "={{ $json.message_id }}"
            }
          ]
        },
        "options": {
          "returnFirstMatch": true
        }
      },
      "id": "7c3a8cc2-f4e6-44d5-b3b1-267f1b5fcc09",
      "name": "Lookup spam",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        2700,
        260
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "continueOnFail": true,
      "notes": "Reads existing message_ids from 'spam' so we don't insert duplicates.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "768be6c5-2b67-4db2-bc8e-e0792c8d8ad6",
              "leftValue": "={{ $json.message_id ? $json.message_id : '' }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "aacadff5-2f51-421a-891d-7922368322cf",
      "name": "Already in spam?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2960,
        260
      ],
      "notes": "Output 0 (TRUE) = duplicate, SKIP append.\nOutput 1 (FALSE) = new message, proceed to append.\nCondition: $json.message_id is non-empty (lookup returned a row).",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "c631ac94-3616-409e-b6de-cc64776f2353",
              "name": "duplicate_skipped",
              "type": "boolean",
              "value": true
            },
            {
              "id": "57c472ba-ebd3-4747-ad3e-64860f7d4880",
              "name": "duplicate_tab",
              "type": "string",
              "value": "spam"
            },
            {
              "id": "dfd9f6ab-2833-4e4e-89e9-4c1c6a7b41be",
              "name": "message_id",
              "type": "string",
              "value": "={{ $json.message_id }}"
            }
          ]
        },
        "options": {
          "include": "all"
        }
      },
      "id": "0512ce99-124f-497a-af0c-041830ecd29e",
      "name": "Duplicate skipped (spam)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [
        3200,
        180
      ],
      "notes": "PDF \u00a73.4: short-circuit with duplicate_skipped=true for spam.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.spreadsheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "spam",
          "cachedResultName": "spam"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "message_id": "={{ $('Parse classification').item.json.message_id }}",
            "received_at": "={{ $('Parse classification').item.json.received_at }}",
            "sender_email": "={{ $('Parse classification').item.json.sender_email }}",
            "subject": "={{ $('Parse classification').item.json.subject_clean }}",
            "confidence": "={{ $('Parse classification').item.json.confidence }}",
            "created_at": "={{ $now.toISO() }}"
          },
          "matchingColumns": []
        },
        "options": {}
      },
      "id": "f8f03e61-652f-4050-beb3-579fa37fdadc",
      "name": "Append spam",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        3200,
        340
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "notes": "Append to 'spam' tab with explicit column mapping.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.spreadsheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "other",
          "cachedResultName": "other"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "message_id",
              "lookupValue": "={{ $json.message_id }}"
            }
          ]
        },
        "options": {
          "returnFirstMatch": true
        }
      },
      "id": "27848863-df6a-4a58-8707-89107c77b6e1",
      "name": "Lookup other",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        2700,
        480
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "continueOnFail": true,
      "notes": "Reads existing message_ids from 'other' so we don't insert duplicates.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "052dde70-1c32-4700-a520-f1b18b4d9930",
              "leftValue": "={{ $json.message_id ? $json.message_id : '' }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "b825d184-45a9-4c8f-8b10-4ec47440947c",
      "name": "Already in other?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2960,
        480
      ],
      "notes": "Output 0 (TRUE) = duplicate, SKIP append.\nOutput 1 (FALSE) = new message, proceed to append.\nCondition: $json.message_id is non-empty (lookup returned a row).",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "d804764a-8016-48e6-819b-e97ec14e88e4",
              "name": "duplicate_skipped",
              "type": "boolean",
              "value": true
            },
            {
              "id": "99dbfab4-ed6f-40b0-9c17-8ab0ff01d602",
              "name": "duplicate_tab",
              "type": "string",
              "value": "other"
            },
            {
              "id": "281fc804-1b2c-4749-88b5-668fd17f4e05",
              "name": "message_id",
              "type": "string",
              "value": "={{ $json.message_id }}"
            }
          ]
        },
        "options": {
          "include": "all"
        }
      },
      "id": "f92bbee1-8c35-498e-9899-76a6fc33eab8",
      "name": "Duplicate skipped (other)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [
        3200,
        400
      ],
      "notes": "PDF \u00a73.4: short-circuit with duplicate_skipped=true for other.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.spreadsheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "other",
          "cachedResultName": "other"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "message_id": "={{ $('Parse classification').item.json.message_id }}",
            "received_at": "={{ $('Parse classification').item.json.received_at }}",
            "sender_email": "={{ $('Parse classification').item.json.sender_email }}",
            "subject": "={{ $('Parse classification').item.json.subject_clean }}",
            "confidence": "={{ $('Parse classification').item.json.confidence }}",
            "created_at": "={{ $now.toISO() }}"
          },
          "matchingColumns": []
        },
        "options": {}
      },
      "id": "a8de2ff7-8072-436a-873b-380ae62bb019",
      "name": "Append other",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        3200,
        560
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "notes": "Append to 'other' tab with explicit column mapping.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $node[\"Config\"].json.spreadsheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "errors",
          "cachedResultName": "errors"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "message_id": "={{ $json.message_id }}",
            "received_at": "={{ $json.received_at }}",
            "error_stage": "Route by category (fallback)",
            "error_message": "={{ 'Unknown category: ' + ($json.category || 'null') + ' | parse_failed=' + ($json.parse_failed || false) }}",
            "raw_payload": "={{ JSON.stringify({category: $json.category, confidence: $json.confidence, reasoning: $json.reasoning, raw_ai_output: ($json.raw_ai_output || '').slice(0, 800)}).slice(0, 1000) }}",
            "created_at": "={{ $now.toISO() }}"
          },
          "matchingColumns": []
        },
        "options": {}
      },
      "id": "e19f1ebe-214f-4f1a-9144-e1acc58ed66e",
      "name": "Log unknown category to errors",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        2960,
        700
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "continueOnFail": true,
      "notes": "Switch fallback path \u2014 writes a row to the 'errors' tab when the classifier emits a category outside LEAD/SUPPORT/SPAM/OTHER. PDF \u00a73.2 verbatim.",
      "notesInFlow": true
    }
  ],
  "connections": {
    "Watch inbox": {
      "main": [
        [
          {
            "node": "Clean email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean email": {
      "main": [
        [
          {
            "node": "Compute preview",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute preview": {
      "main": [
        [
          {
            "node": "Classify email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify email": {
      "main": [
        [
          {
            "node": "Parse classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse classification": {
      "main": [
        [
          {
            "node": "Route by category",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by category": {
      "main": [
        [
          {
            "node": "Lookup leads",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Lookup support",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Lookup spam",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Lookup other",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log unknown category to errors",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lookup leads": {
      "main": [
        [
          {
            "node": "Already in leads?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Already in leads?": {
      "main": [
        [
          {
            "node": "Duplicate skipped (leads)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Append leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lookup support": {
      "main": [
        [
          {
            "node": "Already in support?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Already in support?": {
      "main": [
        [
          {
            "node": "Duplicate skipped (support)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Append support",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lookup spam": {
      "main": [
        [
          {
            "node": "Already in spam?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Already in spam?": {
      "main": [
        [
          {
            "node": "Duplicate skipped (spam)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Append spam",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lookup other": {
      "main": [
        [
          {
            "node": "Already in other?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Already in other?": {
      "main": [
        [
          {
            "node": "Duplicate skipped (other)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Append other",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "Asia/Kolkata",
    "saveExecutionProgress": true,
    "saveManualExecutions": true,
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all"
  },
  "tags": [
    {
      "name": "training"
    },
    {
      "name": "lead-triage"
    },
    {
      "name": "phase-3"
    },
    {
      "name": "batch-may-2026"
    }
  ],
  "meta": {
    "templateCredsSetupCompleted": true,
    "description": "Lead Inbox Triage Bot \u2014 production-style n8n automation. Watches a test Gmail inbox every minute, classifies each new email with an LLM into LEAD/SUPPORT/SPAM/OTHER (strict JSON), logs to a Google Sheets CRM with idempotent appends, drafts (NEVER sends) a personalised reply for genuine sales leads, and posts a Block-Kit lead card to Slack. Failures route to a separate ErrorTrigger sub-workflow that logs to an 'errors' tab and pings Slack.\n\nPhase 3 \u2014 adds Switch routing, per-tab idempotency (Lookup + IF + duplicate_skipped marker), explicit column mappings, and routes the fallback to the errors tab."
  }
}

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

LeadInboxTriageBot_GT. Uses gmailTrigger, openAi, googleSheets, gmail. Event-driven trigger; 36 nodes.

Source: https://github.com/tejash-sr/n8n/blob/5a49f70d329ccafb75ef5898607cf4f1c86b912f/workflows/v4-phase4.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Complete AI-powered sales system Automates lead capture, qualification, and follow-up from multiple channels. AI INTELLIGENCE:

Gmail Trigger, Google Sheets, OpenAI +3
AI & RAG

Overview

Gmail Trigger, Google Drive, OpenAI +4
AI & RAG

Automate your entire invoice processing pipeline with AI-powered OCR, validation, and approval workflows 📄🤖. This n8n automation monitors incoming Gmail invoices, extracts structured data using OCR an

Gmail Trigger, Gmail, OpenAI +3
AI & RAG

A fully automated, AI-powered email assistant built in n8n that reads incoming emails, understands their intent and sentiment, classifies them by category, drafts intelligent context-aware replies, an

Gmail Trigger, Slack, Gmail +2
AI & RAG

Small teams, solo operators, and security-conscious individuals who receive email attachments from external senders. Useful for freelancers, agencies, HR teams, and anyone handling CVs, invoices, or d

Gmail Trigger, HTTP Request, OpenAI +4