This workflow corresponds to n8n.io template #17466 — we link there as the canonical source.
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 →
{
"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
}
]
]
}
}
}
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.
gmailOAuth2httpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow monitors a Gmail inbox, filters out auto-replies and mailing-list noise, classifies each incoming email with DeepSeek (or OpenAI-compatible chat completions), and applies a corresponding Gmail label (order status, product question, refund/complaint, or other)…
Source: https://n8n.io/workflows/17466/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This template is built to be customized for your specific needs. This template has the core logic and n8n node specific references sorted to work with dynamic file names throughout the workflow. Store
📘 Description
Email → AI triage → CRM pipeline (template). Uses gmailTrigger, httpRequest, googleSheets, gmail. Event-driven trigger; 17 nodes.
A 2-branch bidirectional bridge. Email a customer from Close (or any CRM) — they get a blue-bubble iMessage. They reply on iPhone — it lands in your Gmail with the phone number in the subject. Reply i
This workflow polls Gmail for new emails, sends the email text to Groq’s OpenAI-compatible chat completions API (Llama 3.3) to classify it, and then applies the matching Gmail label for Action Require