{
  "id": "tPs2TAM6WsywV8oy",
  "name": "AI Email Triage FREE (Classify + Label, no auto-reply)",
  "tags": [],
  "nodes": [
    {
      "id": "1ee92f46-fe2d-42a2-88cd-7925ac067e33",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -176,
        -64
      ],
      "parameters": {
        "width": 480,
        "height": 816,
        "content": "## AI Email Triage FREE (Classify + Label, no auto-reply)\n\n### How it works\n\nThis workflow monitors Gmail for incoming messages and filters out noise before processing. Emails that pass the conditional check are sent to an AI classification endpoint, then the returned category is parsed and mapped to a Gmail label. The workflow finishes by applying the chosen label to the email, with no automatic reply sent.\n\n### Setup steps\n\n- Connect Gmail credentials for both the Gmail Trigger and Gmail label application node.\n- Configure the Gmail Trigger mailbox, polling behavior, and any search criteria needed for incoming messages.\n- Add the DeepSeek API key or authorization header to the AI HTTP Request node and confirm the model, endpoint, and request body match your account.\n- Create or verify the Gmail labels referenced by the mapping code, and update label IDs/names in the Parse & Map Label node if needed.\n\n### Customization\n\nAdjust the Skip Noise code and IF condition to change which emails are ignored. Update the AI prompt, classification categories, and label mapping logic to match your triage system."
      },
      "typeVersion": 1
    },
    {
      "id": "54d237ae-0768-48c3-97e7-92f93d29784d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        -16
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 320,
        "content": "## Receive and filter email\n\nStarts when a new Gmail message arrives, runs custom noise-filtering logic, and uses a conditional check to decide whether the email should continue to AI processing."
      },
      "typeVersion": 1
    },
    {
      "id": "0c95009e-d564-4ab2-85bf-f3d868fa7c6d",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        -64
      ],
      "parameters": {
        "color": 7,
        "height": 368,
        "content": "## Classify with AI\n\nSends the selected email content to the DeepSeek chat completions API to classify the message for triage."
      },
      "typeVersion": 1
    },
    {
      "id": "fe9f2e22-abaa-4ca1-b109-406e80097ced",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1312,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 336,
        "content": "## Map and apply label\n\nParses the AI classification, maps it to a Gmail label, and applies that label to the original message without sending any auto-reply."
      },
      "typeVersion": 1
    },
    {
      "id": "c15b9de4-e310-495f-908c-32a6cb0fe305",
      "name": "When Email Received",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        432,
        144
      ],
      "parameters": {
        "simple": false,
        "filters": {},
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyX",
              "unit": "minutes",
              "value": 5
            }
          ]
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "53fd430f-9e36-4fcc-ad94-97aceaac7837",
      "name": "Filter Noise Emails",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        144
      ],
      "parameters": {
        "jsCode": "const msg = $input.first().json;\nconst headers = msg.headers || {};\nconst getH = (k) => String(headers[k] || headers[k.toLowerCase()] || '');\nconst from = String(msg.from?.value?.[0]?.address || msg.From || msg.from || '').toLowerCase();\nconst subject = String(msg.subject || msg.Subject || '');\nconst text = String(msg.text || msg.textPlain || msg.snippet || '');\nlet skip = false;\nif (!from) skip = true;\nif (getH('auto-submitted') && getH('auto-submitted') !== 'no') skip = true;\nif (/^(auto:|automatic reply|out of office)/i.test(subject)) skip = true;\nif (getH('list-unsubscribe')) skip = true;\nif (/noreply|no-reply|donotreply|mailer-daemon/.test(from)) skip = true;\nreturn [{ json: { shouldProcess: !skip, from, subject, text: text.slice(0, 4000), messageId: msg.id || '' } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "5d2defe7-ed16-4e4e-9224-84f384ecf186",
      "name": "If Email Should Be Processed",
      "type": "n8n-nodes-base.if",
      "position": [
        864,
        144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "fp1",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.shouldProcess }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "463b89e1-820a-4890-ab9b-51d9b7dd869a",
      "name": "Post to AI Classifier API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1088,
        144
      ],
      "parameters": {
        "url": "https://api.deepseek.com/v1/chat/completions",
        "method": "POST",
        "options": {
          "timeout": 30000
        },
        "jsonBody": "={{ JSON.stringify({ model: 'deepseek-chat', response_format: { type: 'json_object' }, temperature: 0, messages: [ { role: 'system', content: 'Classify this customer support email into exactly one intent: order_status, product_question, refund_or_complaint, other. Respond ONLY with JSON: {\\'intent\\': ...}' }, { role: 'user', content: 'Subject: ' + $json.subject + '\\n\\n' + $json.text } ] }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2,
      "continueOnFail": true
    },
    {
      "id": "be4bf045-f7fe-4c14-8716-c341eaaf643e",
      "name": "Map Classification to Label",
      "type": "n8n-nodes-base.code",
      "position": [
        1360,
        144
      ],
      "parameters": {
        "jsCode": "let intent = 'other';\ntry {\n  const j = JSON.parse($input.first().json.choices?.[0]?.message?.content);\n  if (['order_status','product_question','refund_or_complaint','other'].includes(j.intent)) intent = j.intent;\n} catch (e) {}\n// Map intent -> your Gmail label ID (create labels in Gmail first, then paste IDs\n// or simply select them after import). Placeholder IDs below:\nconst labelMap = {\n  order_status: 'REPLACE_LABEL_ORDER',\n  product_question: 'REPLACE_LABEL_PRODUCT',\n  refund_or_complaint: 'REPLACE_LABEL_REFUND',\n  other: 'REPLACE_LABEL_OTHER'\n};\nreturn [{ json: { intent, labelId: labelMap[intent], messageId: $('Filter Noise Emails').first().json.messageId } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "0830cf11-3149-4655-a462-c87a04d8a4e6",
      "name": "Apply Label in Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1568,
        144
      ],
      "parameters": {
        "labelIds": "={{ [$json.labelId] }}",
        "messageId": "={{ $json.messageId }}",
        "operation": "addLabels"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "f7d8a685-226d-481f-b7c7-df7a4dde98fd",
  "nodeGroups": [],
  "connections": {
    "Filter Noise Emails": {
      "main": [
        [
          {
            "node": "If Email Should Be Processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Email Received": {
      "main": [
        [
          {
            "node": "Filter Noise Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post to AI Classifier API": {
      "main": [
        [
          {
            "node": "Map Classification to Label",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map Classification to Label": {
      "main": [
        [
          {
            "node": "Apply Label in Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Email Should Be Processed": {
      "main": [
        [
          {
            "node": "Post to AI Classifier API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}