{
  "name": "LeadInboxTriageBot",
  "nodes": [
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "simple": false,
        "maxResults": 1,
        "filters": {
          "labelIds": [
            "INBOX"
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.gmailTrigger",
      "typeVersion": 1.4,
      "position": [
        256,
        272
      ],
      "id": "1846e42a-3a45-4b76-91ca-6f995781035d",
      "name": "Watch inbox",
      "retryOnFail": true,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "b689c1ae-5c5e-4987-a130-28914b8aea18",
              "name": "gmail_id",
              "value": "={{ $json.id || \"\" }}",
              "type": "string"
            },
            {
              "id": "c986fb8b-c6ff-4418-883c-a24058176c44",
              "name": "threadId",
              "value": "={{ $json.threadId || \"\" }}",
              "type": "string"
            },
            {
              "id": "2a1e0b09-39e8-4270-9547-9edb40246c8e",
              "name": "messageId",
              "value": "={{ $json.messageId || \"\" }}",
              "type": "string"
            },
            {
              "id": "4277a33b-9365-4105-9eff-169bde281708",
              "name": "subject",
              "value": "={{ $json.subject || \"\" }}",
              "type": "string"
            },
            {
              "id": "485b1e25-85c0-41aa-a189-d1d30946631a",
              "name": "body",
              "value": "={{ $json.text || \"\" }}",
              "type": "string"
            },
            {
              "id": "9c13aae8-5cee-4ea4-9291-eabbfc50db1e",
              "name": "from_email",
              "value": "={{ $json.from.value[0].address || \"\" }}",
              "type": "string"
            },
            {
              "id": "172aba08-06c0-4378-8672-40dd4d881aa5",
              "name": "from_name",
              "value": "={{ $json.from.value[0].name || \"\" }}",
              "type": "string"
            },
            {
              "id": "5835d429-7331-4851-ab41-a4b34c85690d",
              "name": "to_email",
              "value": "={{ $json.to.value[0].address || \"\" }}",
              "type": "string"
            },
            {
              "id": "923710f8-d8b0-411d-babd-97599d0f90d9",
              "name": "sender_domain",
              "value": "={{ ($json.from.value[0].address || \"\").split(\"@\")[1] || \"\" }}",
              "type": "string"
            },
            {
              "id": "a7f28ba7-337c-4e08-92a5-e1cce13b7a41",
              "name": "receivedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            },
            {
              "id": "91f9108a-c2ac-4695-b230-e29c64847d2e",
              "name": "raw_from",
              "value": "={{ $json.from.text || \"\" }}",
              "type": "string"
            }
          ]
        },
        "options": {
          "dotNotation": false
        }
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        528,
        272
      ],
      "id": "fe34b2f9-0465-406a-981d-b86d289d2390",
      "name": "Normalize email payload"
    },
    {
      "parameters": {
        "content": "## Gmail Watcher\n\n**\u2022 Polls inbox every minute\n\u2022 Detects new inbound emails\n\u2022 Filters INBOX messages only\n\u2022 Entry point for workflow execution\n\u2022 Controlled ingestion pipeline**",
        "height": 400,
        "width": 224
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        208,
        32
      ],
      "typeVersion": 1,
      "id": "8430a2f6-5863-4a5f-a04f-eab5d934f0d0",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Normalize & Extract Email\n**\u2022 Extracts structured email fields, Standardizes sender metadata\n\u2022 Builds internal processing schema\n\u2022 Cleans inconsistent Gmail payloads, Prepares data for downstream AI logic** ",
        "height": 400,
        "width": 224
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        464,
        32
      ],
      "typeVersion": 1,
      "id": "861ed6bc-8de7-4969-9310-426f40e19461",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "\n",
        "height": 560,
        "width": 656,
        "color": "#C17B7B"
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        -80
      ],
      "typeVersion": 1,
      "id": "8fed81e1-a680-401a-bdc3-2a011031ff8b",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## PHASE - 1 \n## Email Ingestion & Normalization Pipeline\n\n**\u2022 Gmail inbox polling trigger\n\u2022 Real-time inbound email ingestion\n\u2022 Metadata extraction & normalization\n\u2022 Stable internal payload schema\n\u2022 Sender/domain parsing\n\u2022 Automated message state handling\n\u2022 Preprocessing foundation for AI routing**",
        "height": 256,
        "width": 368,
        "color": "#C17B7B"
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        -384
      ],
      "typeVersion": 1,
      "id": "fd4236e2-8132-4ee8-be40-30daea79dced",
      "name": "Sticky Note5"
    },
    {
      "parameters": {
        "content": "# \ud83d\udfe1 Trigger",
        "height": 80,
        "width": 200,
        "color": 5
      },
      "id": "c958fc98-1398-4c3c-9464-5e2a501fca9d",
      "name": "Section_Trigger",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        224,
        -64
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "b0c22741-7a2e-4ebc-9bfb-bca23749232a",
              "name": "classification_system_prompt",
              "value": "You are an email classification engine for a business operations inbox. Your sole job is to read an incoming email and return a single JSON object \u2014 nothing else.  CATEGORIES: \n- LEAD: A potential customer expressing genuine interest in purchasing, evaluating, or learning more about products/services. Includes RFPs, demo requests, pricing inquiries, partnership enquiries. \n- SUPPORT: An existing customer reporting a bug, asking a how-to question, or requesting account help. \n- SPAM: Unsolicited mass email, cold sales pitches, newsletters, SEO offers, or automated promotional content. \n- OTHER: Internal forwards, calendar invites, automated system notifications, OOO replies, or anything that doesn't fit the above.  \nEXAMPLES:  \nExample 1 \u2014 LEAD: \nEmail: \"Hi, we're evaluating CRM tools for our 50-person sales team. Could you share pricing and arrange a demo?\" \nOutput: {\"category\":\"LEAD\",\"confidence\":0.97,\"reasoning\":\"Explicit demo and pricing request from a decision-maker evaluating tools for a team.\",\"suggested_priority\":\"HIGH\"}  \nExample 2 \u2014 SPAM: \nEmail: \"Boost your SEO rankings by 300%! Click here for our limited-time package.\" \nOutput: {\"category\":\"SPAM\",\"confidence\":0.99,\"reasoning\":\"Unsolicited promotional email with no genuine business intent.\",\n\"suggested_priority\":null}  \n\nOUTPUT RULES:\n1. Return raw valid JSON only. No markdown, no code fences, no explanation.\n2. The JSON must contain exactly these four fields:\n   - category\n   - confidence\n   - reasoning\n   - suggested_priority\n3. category must be exactly one of:\n   LEAD, SUPPORT, SPAM, OTHER\n4. confidence must be a float between 0.0 and 1.0\n5. reasoning must be one sentence maximum\n6. suggested_priority must be exactly one of:\n   HIGH, MEDIUM, LOW\n7. \nsuggested_priority must be null when category is not LEAD\n8. If unsure, default to OTHER with low confidence\n9. The response must be valid parsable JSON\n10. Never add extra fields\n11. CRITICAL: The JSON must start with { and end with }. \n    The field  \"suggested_priority\" must be with value exactly \n    one of these strings: HIGH, MEDIUM, LOW.\nmust be null when category is not LEAD.",
              "type": "string"
            },
            {
              "id": "0eb85692-b484-48e7-a66a-0f31711e99cd",
              "name": "classification_user_prompt",
              "value": "=Classify the following email.\nFrom domain: {{$json.sender_domain}} \nSubject: {{$json.subject_clean}} \nBody: {{$json.body_clean}}  \nReturn a single JSON object following the rules in the system prompt. \nReturn only: {\"category\":\"...\",\"confidence\":...,\"reasoning\":\"...\",\"suggested_priority\":...}\nNo other text.",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1232,
        288
      ],
      "id": "3f190471-445b-4e5c-9d5a-ebaf936223dc",
      "name": "Prompts"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $json;\n\n// 1. Clean Subject\nconst subject_clean = (item.subject || '')\n  .replace(/^(Re:|Fwd:|FW:|RE:|FWD:|AW:|Aw:)\\s*/gi, '').trim();\n\n// 2. Clean Body\nlet body = item.body || '';\n\n// Remove reply chains\nconst replyStopPatterns = [\n  /On .{5,130}(wrote:|\u00e9crit:)/s,\n  /-----+\\s*(Original Message|Forwarded Message|BEGIN)\\s*-----+/i,\n /^From:\\s.*$\\n^Sent:\\s.*$/gim\n];\nfor (const p of replyStopPatterns) {\n  const idx = body.search(p);\n  if (idx > 40) { body = body.substring(0, idx); break; }\n}\n\n// Remove signatures\nconst sigStopPatterns = [\n  /\\n--\\s*\\n/i,\n  /\\n(Best regards|Kind regards|Warm regards|Regards|Sincerely|Cheers)\\s*,?\\s*\\n/i,\n  /\\n[-\u2014]{3,}/\n];\nfor (const p of sigStopPatterns) {\n  const idx = body.search(p);\n  if (idx > 20) { body = body.substring(0, idx); break; }\n}\n\nconst body_clean = body\n// Normalize whitespace while preserving readability\n  .replace(/[ \\t]+/g, \" \")\n  .replace(/\\n{3,}/g, \"\\n\\n\")\n  .trim();\n\n\n\n// 3. Extract email/name from From field\nconst sender_email = (item.from_email || \"\").toLowerCase();\n\nconst sender_domain = sender_email.split(\"@\")[1] || \"\";\n\nconst sender_name = (item.from_name || \"\").trim();\n\nconst sender_first_name = sender_name.split(/\\s+/)[0] || \"\";\n\n// 4. Preview\nconst body_preview = body_clean.substring(0, 500);\n\nreturn {\n  ...item,\n  subject_clean,\n  body_clean,\n  body_preview,\n  sender_email,\n  sender_domain,\n  sender_name,\n  sender_first_name\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1008,
        288
      ],
      "id": "1bced48e-5dfe-42e1-8d0e-416fff85f4cf",
      "name": "Clean Email"
    },
    {
      "parameters": {
        "content": "",
        "height": 576,
        "width": 1200,
        "color": "#BFB5B5"
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        -80
      ],
      "typeVersion": 1,
      "id": "ac672e9f-c56c-4f9e-976e-0fc328688c01",
      "name": "Sticky Note8"
    },
    {
      "parameters": {
        "content": "## Parse Classification\n**\u2022 Parses AI-generated JSON safely\n\u2022 Validates categories & confidence ranges\n\u2022 Applies schema normalization rules\n\u2022 Prevents malformed downstream routing\n\u2022 Fallback protection for parsing failures**",
        "height": 416,
        "width": 256,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1776,
        32
      ],
      "typeVersion": 1,
      "id": "0f02fad7-a788-4b34-8c15-8464e851f8b8",
      "name": "Sticky Note7"
    },
    {
      "parameters": {
        "content": "## Classify Email\n**\u2022 LLM-based intent analysis\n\u2022 Categorizes emails into:\n  LEAD, SUPPORT, SPAM, OTHER\n\u2022 Generates confidence scoring\n\u2022 Infers operational lead priority\n\u2022 Optimized for structured automation workflows** ",
        "height": 416,
        "width": 304,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1440,
        32
      ],
      "typeVersion": 1,
      "id": "686c66e7-42f3-4884-b7e7-524049d68954",
      "name": "Sticky Note6"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\nconst original = $('Clean Email').item.json;\nlet parse_failed = false;\nlet parsed;\n\ntry {\n  const raw =\n    item.output?.[1]?.content?.[0]?.text   // Responses API\n    ?? item.choices?.[0]?.message?.content  // Chat Completions API\n    ?? item.text\n    ?? \"\";\n  \n\n  const match = raw.match(/\\{[\\s\\S]*\\}/);\n  parsed = JSON.parse(match ? match[0] : raw);\n} catch {\n  parse_failed = true;\n  parsed = { category: \"OTHER\", confidence: 0, reasoning: \"Failed to parse model response.\", suggested_priority: null };\n}\n\nconst ALLOWED_CAT = [\"LEAD\", \"SUPPORT\", \"SPAM\", \"OTHER\"];\nconst ALLOWED_PRI = [\"LOW\", \"MEDIUM\", \"HIGH\"];\n\nif (!ALLOWED_CAT.includes(parsed.category)) { parsed.category = \"OTHER\"; parse_failed = true; }\n\nparsed.confidence = Math.max(0, Math.min(1, Number(parsed.confidence) || 0));\nparsed.reasoning = typeof parsed.reasoning === \"string\" ? parsed.reasoning.slice(0, 300) : \"No reasoning provided.\";\n\nif (parsed.category !== \"LEAD\") {\n  parsed.suggested_priority = null;\n} else if (!ALLOWED_PRI.includes(parsed.suggested_priority)) {\n  parsed.suggested_priority = \"MEDIUM\";\n}\nreturn {\n  ...original,\n  classification: {\n    category: parsed.category,\n    confidence: parsed.confidence,\n    reasoning: parsed.reasoning,\n    suggested_priority: parsed.suggested_priority\n  },\n  parse_failed,\n  classified_at: new Date().toISOString()\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1856,
        288
      ],
      "id": "d71ac091-f391-4a55-b8c2-c8f6b4ca1e75",
      "name": "Parse classification"
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "llama-3.3-70b-versatile",
          "mode": "list",
          "cachedResultName": "LLAMA-3.3-70B-VERSATILE"
        },
        "responses": {
          "values": [
            {
              "role": "system",
              "content": "={{ $('Prompts').item.json.classification_system_prompt}}"
            },
            {
              "content": "={{ $('Prompts').item.json.classification_user_prompt }}"
            }
          ]
        },
        "simplify": false,
        "builtInTools": {},
        "options": {
          "instructions": "",
          "store": false,
          "temperature": 0
        }
      },
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 2.3,
      "position": [
        1472,
        288
      ],
      "id": "74506c9e-67b6-4e1d-a5cc-0917ddeaa89f",
      "name": "Classify email",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## Prompts\n**\u2022 Centralized system & user prompts\n\u2022 Deterministic structured AI instructions\n\u2022 Strict JSON response enforcement\n\u2022 Category & priority constraints\n\u2022 Consistent downstream parsing behavior** ",
        "height": 416,
        "width": 224,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1184,
        32
      ],
      "typeVersion": 1,
      "id": "29125abc-d180-4f29-bf62-0ee6a2fc449c",
      "name": "Sticky Note9"
    },
    {
      "parameters": {
        "operation": "markAsRead",
        "messageId": "={{ $json.gmail_id }}"
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        1872,
        736
      ],
      "id": "130bd576-ad0d-4391-bea4-7a20cb830fd2",
      "name": "Mark a message as read1",
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## Mark a message as read\n**Marks successfully processed Gmail messages as read using the Gmail internal message ID** ",
        "height": 352,
        "width": 224
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1808,
        544
      ],
      "typeVersion": 1,
      "id": "b96c64c6-60b3-436e-9536-c71aedcc1844",
      "name": "Sticky Note13"
    },
    {
      "parameters": {
        "content": "## PHASE - 2\n\n## AI Classification & Semantic Processing Pipeline\n\n**\u2022 Email content sanitization\n\u2022 Semantic preprocessing & normalization\n\u2022 Centralized prompt orchestration\n\u2022 LLM-powered email classification\n\u2022 Structured JSON response enforcement\n\u2022 Confidence scoring & priority inference\n\u2022 Classification validation & fallback handling\n\u2022 Safe downstream AI routing preparation**",
        "height": 304,
        "width": 368,
        "color": "#BFB5B5"
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1312,
        -400
      ],
      "typeVersion": 1,
      "id": "cb12479a-a27c-45c1-8387-631c9912e4c7",
      "name": "Sticky Note10"
    },
    {
      "parameters": {
        "content": "# \ud83d\udfe0 Pre-process",
        "height": 80,
        "width": 280,
        "color": 4
      },
      "id": "861b613d-9bfb-49ed-a81b-7175b7b37f38",
      "name": "Section_Preprocess",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        688,
        -64
      ]
    },
    {
      "parameters": {
        "content": "## Clean Email\n**\u2022 Removes reply chains & signatures\n\u2022 Sanitizes noisy email content\n\u2022 Extracts latest meaningful context\n\u2022 Reduces token usage & prompt noise\n\u2022 Improves downstream classification accuracy** ",
        "height": 416,
        "width": 224,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        944,
        32
      ],
      "typeVersion": 1,
      "id": "ba745542-b81e-4e95-8a7d-7b4cf41d86c3",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "content": "# \ud83d\udfe6 Classify",
        "height": 80,
        "width": 232,
        "color": 6
      },
      "id": "95183737-8559-4f87-99f1-a82f580a8282",
      "name": "Section_Classify",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1648,
        -64
      ]
    }
  ],
  "connections": {
    "Watch inbox": {
      "main": [
        [
          {
            "node": "Normalize email payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize email payload": {
      "main": [
        [
          {
            "node": "Clean Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prompts": {
      "main": [
        [
          {
            "node": "Classify email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean Email": {
      "main": [
        [
          {
            "node": "Prompts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse classification": {
      "main": [
        [
          {
            "node": "Mark a message as read1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify email": {
      "main": [
        [
          {
            "node": "Parse classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "98f89cfb-080f-4a0a-8363-99ff6a30dac7",
  "id": "frIKH7E4frasLmeS",
  "tags": [
    {
      "updatedAt": "2026-05-17T12:35:49.965Z",
      "createdAt": "2026-05-17T12:35:49.965Z",
      "id": "9DdkRio6FFt0KDAN",
      "name": "phase-1"
    },
    {
      "updatedAt": "2026-05-18T20:20:17.612Z",
      "createdAt": "2026-05-18T20:20:17.612Z",
      "id": "DwZiobfGzVfGQlJn",
      "name": "batch26_intern"
    },
    {
      "updatedAt": "2026-05-17T12:35:49.949Z",
      "createdAt": "2026-05-17T12:35:49.949Z",
      "id": "HP8bIX2EyHTE8tjn",
      "name": "training"
    },
    {
      "updatedAt": "2026-05-20T00:07:08.323Z",
      "createdAt": "2026-05-20T00:07:08.323Z",
      "id": "jbnaPX0FlZoNVGGj",
      "name": "LeadInbox"
    },
    {
      "updatedAt": "2026-05-20T00:07:08.303Z",
      "createdAt": "2026-05-20T00:07:08.303Z",
      "id": "sDZhwhPKimwdhRPb",
      "name": "Training"
    }
  ]
}