This workflow corresponds to n8n.io template #17236 — 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": "Kk3UhnfOlQyVrijR",
"name": "Email Sorter",
"tags": [],
"nodes": [
{
"id": "f1bd2eba-b724-432f-bc01-cdbeddd32e81",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-736,
2048
],
"parameters": {
"width": 480,
"height": 896,
"content": "## Email Sorter\n### How it works\n\n1. The workflow triggers on new Gmail messages.\n2. It retrieves the full email message.\n3. The email is pre-classified using custom rules code.\n4. Based on the rules, it routes the email to appropriate processing branches.\n5. Emails are labeled or processed through OpenAI to decide action.\n6. Emails are scheduled for deletion if classified for deletion.\n\n### Setup steps\n\n- [ ] Set up Gmail API credentials for the Gmail Trigger and Gmail nodes.\n- [ ] Configure OpenAI credentials for handling classification.\n- [ ] Define rules within the 'Pre-Classify (rules)' and 'Route by rules' nodes.\n\n### Customization\n\nAdjust the rules in the 'Pre-Classify (rules)' and 'Route by rules' nodes to match your specific needs."
},
"typeVersion": 1
},
{
"id": "504182f9-3750-4330-88c4-2e4617a80e20",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
-176,
2368
],
"parameters": {
"color": 7,
"width": 416,
"height": 304,
"content": "## Email receipt and retrieval\n\nTriggers on a new email and retrieves the full message."
},
"typeVersion": 1
},
{
"id": "2b019d04-c4ba-4f76-a728-be2725a5b654",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
272,
2320
],
"parameters": {
"color": 7,
"width": 448,
"height": 400,
"content": "## Preprocessing and routing\n\nPre-classifies emails based on rules and routes them to relevant processing branches."
},
"typeVersion": 1
},
{
"id": "7037081b-8794-4bbc-ba57-5e880fa11205",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
784,
2048
],
"parameters": {
"color": 7,
"height": 640,
"content": "## Team and meeting labeling\n\nLabels emails related to team and meetings."
},
"typeVersion": 1
},
{
"id": "ad638231-961b-4610-ab73-3a3eae4ef750",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"position": [
768,
2720
],
"parameters": {
"color": 7,
"width": 656,
"height": 336,
"content": "## OpenAI classification process\n\nUses OpenAI to classify emails as LEAD, TRASH, or REVIEW and routes them accordingly."
},
"typeVersion": 1
},
{
"id": "7856c9fc-5c3c-4360-a8a0-7ff6896334d9",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"position": [
1472,
2368
],
"parameters": {
"color": 7,
"height": 688,
"content": "## Labeling and pending deletion\n\nLabels emails as leads, for review, or schedules them for deletion."
},
"typeVersion": 1
},
{
"id": "9360876c-e8c7-4e61-ae65-54578bfb64b3",
"name": "Sticky Note13",
"type": "n8n-nodes-base.stickyNote",
"position": [
1744,
2784
],
"parameters": {
"color": 7,
"width": 624,
"height": 272,
"content": "## Scheduled email deletion\n\nHandles scheduling and moving emails to trash."
},
"typeVersion": 1
},
{
"id": "1f5f8695-9851-485d-b81e-b16a44fbe1bc",
"name": "When Email Received1",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
-128,
2496
],
"parameters": {
"simple": false,
"filters": {},
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyX",
"unit": "minutes",
"value": 5
}
]
}
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "b5356e12-f626-428a-9b2b-d6c531600c1b",
"name": "Fetch Full Email Message1",
"type": "n8n-nodes-base.gmail",
"position": [
96,
2496
],
"parameters": {
"simple": false,
"options": {},
"messageId": "={{ $json.id }}",
"operation": "get"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "bf40f558-6218-48ec-b0a3-1b80488edb4a",
"name": "Execute Pre-Classify Rules1",
"type": "n8n-nodes-base.code",
"position": [
320,
2496
],
"parameters": {
"jsCode": "\n// \u2500\u2500 Email sorter \u2014 deterministic pre-classification \u2500\u2500\n// Decide everything rules CAN decide, so the LLM only sees genuinely ambiguous mail.\n\nconst TEAM = new Set([\n \"user@example.com\",\n \"user@example.com\",\n \"user@example.com\",\n \"user@example.com\",\n]);\n// Website form fill-outs \u2014 a SEPARATE workflow already replies to these. Don't touch.\nconst FORM_SENDERS = new Set([\"user@example.com\"]);\n\nfunction extractEmail(f){\n if(!f) return \"\";\n if(typeof f===\"object\"){\n if(Array.isArray(f.value)&&f.value[0]&&f.value[0].address) return String(f.value[0].address).toLowerCase();\n if(f.address) return String(f.address).toLowerCase();\n if(f.text) f=f.text;\n }\n const s=String(f);\n const m=s.match(/[a-z0-9._%+\\-]+@[a-z0-9.\\-]+\\.[a-z]{2,}/i);\n return (m?m[0]:s).trim().toLowerCase();\n}\nfunction extractName(f){\n if(!f) return \"\";\n if(typeof f===\"object\"){\n if(Array.isArray(f.value)&&f.value[0]&&f.value[0].name) return String(f.value[0].name).trim();\n if(f.text) f=f.text;\n }\n const m=String(f).match(/^\\s*\"?([^\"<]+?)\"?\\s*</);\n return (m?m[1]:\"\").trim();\n}\nfunction getHeader(j,name){\n const n=name.toLowerCase();\n const h=j.headers||{};\n for(const k in h){ if(String(k).toLowerCase()===n) return String(h[k]); }\n if(Array.isArray(j.headerLines)){\n for(const line of j.headerLines){\n if(line && String(line.key||\"\").toLowerCase()===n) return String(line.line||\"\");\n }\n }\n return \"\";\n}\n\nconst LLM_SYSTEM = [\n\"You are an email triage engine for our company. Team mail, website form submissions,\",\n\"calendar/scheduling mail, verification codes, and obvious bulk/marketing mail have\",\n\"ALREADY been filtered out before you see this. Your ONLY job: decide, using ONLY the\",\n\"text explicitly in the email, whether it is LEAD, TRASH, or REVIEW.\",\n\"\",\n\"LEAD - a real person having (or opening) a genuine business conversation with\",\n\" our company about our product/service: a prospective or existing customer, a\",\n\" reply in an ongoing sales/support thread, a real inbound question about what\",\n\" we do, pricing, a demo, a quote, next steps, a proposal. Test: a human wrote\",\n\" this TO US, ABOUT OUR business, expecting a real reply from a person.\",\n\"TRASH - anything else: cold outreach or pitches aimed AT us, vendor/agency/SEO/\",\n\" recruiting solicitation, promotions, product-update blasts, app/social\",\n\" notifications, receipts unrelated to a live deal, crypto/spam. Sign: THEY are\",\n\" selling to US, or no human reply is expected.\",\n\"REVIEW - use ONLY when you truly cannot tell LEAD from TRASH from the text given. When\",\n\" unsure, choose REVIEW instead of guessing. REVIEW is never deleted.\",\n\"\",\n\"Rules:\",\n\"- Judge from quoted evidence only; never assume unstated prior context.\",\n\"- 'Re:' / 'Fwd:' alone does NOT make something a LEAD.\",\n\"- Someone selling something to our company is TRASH even if personalized to us.\",\n\"\",\n\"Examples:\",\n\"- 'Saw your site - can you send pricing for 25 users and availability for a demo next\",\n\" week?' -> LEAD (inbound buyer intent).\",\n\"- 'Following up on the proposal you sent - the team approved, what are next steps?'\",\n\" -> LEAD (ongoing deal).\",\n\"- 'I help SaaS companies book 30 meetings/month. Open to a quick call?' -> TRASH\",\n\" (they are selling to us).\",\n\"- 'Your AWS invoice for June is ready.' -> TRASH (transactional, no reply expected).\",\n\"- 'Quick question about what you guys do' from an unknown personal address, nothing\",\n\" else -> REVIEW (ambiguous).\",\n\"\",\n\"Output ONLY this JSON (no prose, no markdown fences):\",\n'{\"evidence\":\"<exact quote(s) from the email, or empty>\",\"category\":\"LEAD|TRASH|REVIEW\",\"summary\":\"<one factual sentence stating only what the email says>\"}'\n].join(\"\\n\");\n\nconst out=[];\nconst items=$input.all();\nfor(let i=0;i<items.length;i++){\n const j=items[i].json;\n\n const from_email = extractEmail(j.from);\n const from_name = extractName(j.from) || String(j.fromName ?? \"\");\n const subject = String(j.subject ?? \"\").trim();\n const bodyFull = String(j.text ?? j.textPlain ?? j.snippet ?? \"\").trim();\n const body = bodyFull.slice(0,4000);\n const id = String(j.id ?? j.messageId ?? \"\");\n const threadId = String(j.threadId ?? \"\");\n const received_ms= j.internalDate ? String(j.internalDate)\n : (j.date ? String(new Date(j.date).getTime()) : \"\");\n const domain = from_email.split(\"@\")[1] || \"\";\n const localpart = from_email.split(\"@\")[0] || \"\";\n const listUnsub = getHeader(j,\"List-Unsubscribe\");\n const precedence = getHeader(j,\"Precedence\").toLowerCase();\n const autoSub = getHeader(j,\"Auto-Submitted\").toLowerCase();\n const hay = (subject+\" \"+bodyFull).toLowerCase();\n const subjLc = subject.toLowerCase();\n\n let decision=\"UNKNOWN\", reason=\"\";\n\n if(TEAM.has(from_email)){ decision=\"TEAM\"; reason=\"Sender on internal team list.\"; }\n else if(FORM_SENDERS.has(from_email)){ decision=\"FORM\"; reason=\"Website form submission (separate Forms workflow).\"; }\n else {\n const schedulerSender = domain===\"calendly.com\"\n || from_email===\"user@example.com\"\n || domain.endsWith(\"calendar.google.com\")\n || domain===\"zoom.us\";\n const meetingSubject =\n /^(invitation:|updated invitation:|accepted:|declined:|tentatively accepted:|canceled event:|cancelled event:|new event:)/.test(subjLc)\n || /\\bhas been scheduled\\b/.test(hay)\n || /\\bcalendly\\b/.test(hay)\n || /(reschedule|meeting invitation|scheduled a meeting|booking confirmed|invite you to a meeting)/.test(hay);\n const meetingLink =\n /calendly\\.com\\//.test(hay) || /meet\\.google\\.com\\//.test(hay)\n || /zoom\\.us\\/j\\//.test(hay) || /teams\\.microsoft\\.com\\/l\\/meetup/.test(hay);\n\n if(schedulerSender||meetingSubject||meetingLink){ decision=\"MEETING\"; reason=\"Calendar / scheduling signal.\"; }\n else {\n const otp =\n /\\b(verification code|one[- ]?time (?:code|password)|otp|security code|login code|2fa|two[- ]?factor|confirm your email|reset your password)\\b/.test(hay)\n || /\\b\\d{4,8}\\b[^\\n]{0,40}(code|verify|verification)/.test(hay);\n const bulkHeader = !!listUnsub || precedence===\"bulk\" || precedence===\"list\" || (autoSub && autoSub!==\"no\");\n const bulkSender = /^(no-?reply|do-?not-?reply|donotreply|noreply|mailer-daemon|bounce|bounces|notifications?|newsletter|marketing|mailer|postmaster)$/.test(localpart);\n if(otp){ decision=\"TRASH\"; reason=\"Verification / OTP / transactional security email (not a keeper).\"; }\n else if(bulkHeader||bulkSender){ decision=\"TRASH\"; reason=\"Bulk / marketing / no-reply signal (List-Unsubscribe or bulk sender).\"; }\n }\n }\n\n const llm_user =\n \"FROM_NAME: \"+from_name+\"\\n\"+\n \"FROM_EMAIL: \"+from_email+\"\\n\"+\n \"SUBJECT: \"+subject+\"\\n\"+\n \"BODY:\\n\"+body;\n\n out.push({\n json:{ id, threadId, from_email, from_name, subject, body, received_ms, decision, reason, llm_system:LLM_SYSTEM, llm_user },\n pairedItem:i\n });\n}\nreturn out;\n"
},
"typeVersion": 2
},
{
"id": "6cf5d404-18c6-445e-92b0-6d195f3112ee",
"name": "Route by Pre-Classification1",
"type": "n8n-nodes-base.switch",
"position": [
576,
2448
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "TEAM",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "f9946566bb114b00",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.decision }}",
"rightValue": "TEAM"
}
]
},
"renameOutput": true
},
{
"outputKey": "FORM",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "69e663b0070546b1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.decision }}",
"rightValue": "FORM"
}
]
},
"renameOutput": true
},
{
"outputKey": "MEETING",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "14121b2278ba42b4",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.decision }}",
"rightValue": "MEETING"
}
]
},
"renameOutput": true
},
{
"outputKey": "UNKNOWN",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "103f6b79c2834987",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.decision }}",
"rightValue": "UNKNOWN"
}
]
},
"renameOutput": true
},
{
"outputKey": "TRASH",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "a54d39543ba54c90",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.decision }}",
"rightValue": "TRASH"
}
]
},
"renameOutput": true
}
]
},
"options": {}
},
"typeVersion": 3
},
{
"id": "097e2e64-be21-4781-9ae7-b99da32c5454",
"name": "Label as Team1",
"type": "n8n-nodes-base.gmail",
"position": [
832,
2224
],
"parameters": {
"labelIds": [
"LABEL_ID_TEAM_REPLACE_ME"
],
"messageId": "={{ $json.id }}",
"operation": "addLabels"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "de94c6c5-a460-4c07-95c0-8810b1f61790",
"name": "Skip Processing1",
"type": "n8n-nodes-base.noOp",
"position": [
832,
2384
],
"parameters": {},
"typeVersion": 1
},
{
"id": "c68fcc55-a295-48e2-bfa6-0464a4644af5",
"name": "Label as Meetings1",
"type": "n8n-nodes-base.gmail",
"position": [
832,
2528
],
"parameters": {
"labelIds": [
"LABEL_ID_MEETINGS_REPLACE_ME"
],
"messageId": "={{ $json.id }}",
"operation": "addLabels"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "7e6491d6-31cd-466c-bbb7-5a2a00b99830",
"name": "OpenAI Lead/Trash Classifier1",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
816,
2864
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultUrl": "",
"cachedResultName": ""
},
"options": {
"temperature": 0
},
"messages": {
"values": [
{
"role": "system",
"content": "={{ $json.llm_system }}"
},
{
"content": "={{ $json.llm_user }}"
}
]
}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.8
},
{
"id": "b5be1141-7111-4c8a-86b1-7b1cfd86627e",
"name": "Parse Classification Result1",
"type": "n8n-nodes-base.code",
"position": [
1104,
2864
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "\n// Defensive parse. FAIL SAFE: anything unreadable -> REVIEW (never TRASH).\nconst raw = $json;\nlet content = \"\";\nif(raw && typeof raw===\"object\"){\n content = raw.content ?? (raw.message && raw.message.content) ?? raw.text ?? raw.output_text\n ?? (raw.choices && raw.choices[0] && raw.choices[0].message && raw.choices[0].message.content) ?? \"\";\n if(!content && Array.isArray(raw.output)){\n for(const o of raw.output){\n if(o && Array.isArray(o.content)){\n for(const c of o.content){ if(c && c.text){ content = c.text; break; } }\n }\n }\n }\n}\nlet parsed = null;\nif(!content && raw && (raw.category || raw.evidence)) parsed = raw; // node already returned parsed JSON\nif(!parsed){\n try{\n const cleaned = String(content).replace(/^```(?:json)?/i,\"\").replace(/```$/i,\"\").trim();\n parsed = JSON.parse(cleaned);\n }catch(e){\n parsed = { category:\"REVIEW\", evidence:\"\", summary:\"LLM output unparseable - routed to Review.\" };\n }\n}\nlet cat = String(parsed.category || \"REVIEW\").toUpperCase();\nif(![\"LEAD\",\"TRASH\",\"REVIEW\"].includes(cat)) cat = \"REVIEW\";\n\nconst src = $('Execute Pre-Classify Rules1').item.json;\nreturn { json:{\n decision: cat,\n evidence: parsed.evidence || \"\",\n summary: parsed.summary || \"\",\n id: src.id, threadId: src.threadId,\n from_email: src.from_email, from_name: src.from_name,\n subject: src.subject, received_ms: src.received_ms\n}};\n"
},
"typeVersion": 2
},
{
"id": "86ed3886-1a34-4ed0-b1d8-26503cea1865",
"name": "Route by LLM Classification1",
"type": "n8n-nodes-base.switch",
"position": [
1280,
2848
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "LEAD",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "f6cd891f99604f13",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.decision }}",
"rightValue": "LEAD"
}
]
},
"renameOutput": true
},
{
"outputKey": "REVIEW",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "f743bbe7414b414b",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.decision }}",
"rightValue": "REVIEW"
}
]
},
"renameOutput": true
},
{
"outputKey": "TRASH",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "4ec8043efd444fb4",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.decision }}",
"rightValue": "TRASH"
}
]
},
"renameOutput": true
}
]
},
"options": {}
},
"typeVersion": 3
},
{
"id": "50b5698c-c29c-4b68-8cee-df50b3995ade",
"name": "Label as Leads1",
"type": "n8n-nodes-base.gmail",
"position": [
1520,
2560
],
"parameters": {
"labelIds": [
"LABEL_ID_LEADS_REPLACE_ME"
],
"messageId": "={{ $json.id }}",
"operation": "addLabels"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "83b5d984-2be5-48b7-ac01-d1eef1c2b90d",
"name": "Label for Review1",
"type": "n8n-nodes-base.gmail",
"position": [
1520,
2720
],
"parameters": {
"labelIds": [
"LABEL_ID_REVIEW_REPLACE_ME"
],
"messageId": "={{ $json.id }}",
"operation": "addLabels"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "31fc7b36-9caa-4a50-abc1-c6bd3f504c5d",
"name": "Calculate Deletion Time1",
"type": "n8n-nodes-base.code",
"position": [
1520,
2896
],
"parameters": {
"jsCode": "\n// Compute WHEN a TRASH email should be deleted, per the rules:\n// - Fri / Sat / Sun received -> next Monday 13:00\n// - Mon-Thu received AFTER 17:00 -> next day 13:00\n// - Mon-Thu received before 17:00 -> +2 hours\n// - Guard: never land on Fri/Sat/Sun -> push to next Monday 13:00\n// Produces an ABSOLUTE instant (delete_at_iso) so the Wait node resumes exactly then.\n\nconst TZ = 'America/New_York'; // <-- CHANGE to your business timezone (e.g. 'Europe/London', 'America/Toronto', 'Asia/Makassar')\nconst DT = (typeof DateTime !== 'undefined') ? DateTime : $now.constructor;\n\nfunction recvOf(j){\n if(j.received_ms){ const n=Number(j.received_ms); if(!isNaN(n)&&n>0) return DT.fromMillis(n,{zone:TZ}); }\n if(j.internalDate){ const n=Number(j.internalDate); if(!isNaN(n)&&n>0) return DT.fromMillis(n,{zone:TZ}); }\n if(j.date){ const d=new Date(j.date); if(!isNaN(d.getTime())) return DT.fromMillis(d.getTime(),{zone:TZ}); }\n return $now.setZone(TZ);\n}\nfunction nextMonday1pm(dt){\n let d=dt;\n while(d.weekday!==1) d=d.plus({days:1});\n return d.set({hour:13,minute:0,second:0,millisecond:0});\n}\n\nconst out=[];\nconst items=$input.all();\nfor(let i=0;i<items.length;i++){\n const j=items[i].json;\n const recv=recvOf(j);\n const dow=recv.weekday, hour=recv.hour; // Mon=1 .. Sun=7\n let target;\n if(dow>=5){ // Fri / Sat / Sun\n target=nextMonday1pm(recv);\n } else if(hour>=17){ // Mon-Thu after 5pm\n target=recv.plus({days:1}).set({hour:13,minute:0,second:0,millisecond:0});\n } else { // Mon-Thu before 5pm\n target=recv.plus({hours:2});\n }\n if(target.weekday>=5) target=nextMonday1pm(target); // never delete on Fri/Sat/Sun\n const nowZ=$now.setZone(TZ);\n if(target < nowZ) target=nowZ.plus({seconds:30}); // never schedule in the past\n\n out.push({\n json:{ ...j, delete_at_iso: target.toISO(), delete_at_human: target.toFormat(\"cccc, LLL d yyyy, h:mm a ZZZZ\") },\n pairedItem:i\n });\n}\nreturn out;\n"
},
"typeVersion": 2
},
{
"id": "5617c5ce-2609-4322-8746-dc9f39bd6809",
"name": "Label Pending Deletion1",
"type": "n8n-nodes-base.gmail",
"position": [
1792,
2896
],
"parameters": {
"labelIds": [
"LABEL_ID_PENDING_DELETE_REPLACE_ME"
],
"messageId": "={{ $json.id }}",
"operation": "addLabels"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "e0e37918-82e7-445c-80b7-690f6af6f33a",
"name": "Wait for Deletion Time1",
"type": "n8n-nodes-base.wait",
"position": [
2000,
2896
],
"parameters": {
"resume": "specificTime",
"dateTime": "={{ $('Calculate Deletion Time1').item.json.delete_at_iso }}"
},
"typeVersion": 1.1
},
{
"id": "d5c6661b-f9d6-47b2-b564-4bccfa604468",
"name": "Trash Email Thread1",
"type": "n8n-nodes-base.gmail",
"position": [
2224,
2896
],
"parameters": {
"resource": "thread",
"threadId": "={{ $('Calculate Deletion Time1').item.json.threadId }}",
"operation": "trash"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "7ff66982-6639-4dc2-9cba-c885543125ce",
"nodeGroups": [],
"connections": {
"When Email Received1": {
"main": [
[
{
"node": "Fetch Full Email Message1",
"type": "main",
"index": 0
}
]
]
},
"Label Pending Deletion1": {
"main": [
[
{
"node": "Wait for Deletion Time1",
"type": "main",
"index": 0
}
]
]
},
"Wait for Deletion Time1": {
"main": [
[
{
"node": "Trash Email Thread1",
"type": "main",
"index": 0
}
]
]
},
"Calculate Deletion Time1": {
"main": [
[
{
"node": "Label Pending Deletion1",
"type": "main",
"index": 0
}
]
]
},
"Fetch Full Email Message1": {
"main": [
[
{
"node": "Execute Pre-Classify Rules1",
"type": "main",
"index": 0
}
]
]
},
"Execute Pre-Classify Rules1": {
"main": [
[
{
"node": "Route by Pre-Classification1",
"type": "main",
"index": 0
}
]
]
},
"Parse Classification Result1": {
"main": [
[
{
"node": "Route by LLM Classification1",
"type": "main",
"index": 0
}
]
]
},
"Route by LLM Classification1": {
"main": [
[
{
"node": "Label as Leads1",
"type": "main",
"index": 0
}
],
[
{
"node": "Label for Review1",
"type": "main",
"index": 0
}
],
[
{
"node": "Calculate Deletion Time1",
"type": "main",
"index": 0
}
]
]
},
"Route by Pre-Classification1": {
"main": [
[
{
"node": "Label as Team1",
"type": "main",
"index": 0
}
],
[
{
"node": "Skip Processing1",
"type": "main",
"index": 0
}
],
[
{
"node": "Label as Meetings1",
"type": "main",
"index": 0
}
],
[
{
"node": "OpenAI Lead/Trash Classifier1",
"type": "main",
"index": 0
}
],
[
{
"node": "Calculate Deletion Time1",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Lead/Trash Classifier1": {
"main": [
[
{
"node": "Parse Classification Result1",
"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.
gmailOAuth2openAiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow monitors Gmail for new messages, applies rule-based pre-classification, and uses OpenAI to categorize ambiguous emails as lead, review, or trash, then applies Gmail labels and schedules automatic deletion for trash threads. Triggers every 5 minutes when a new Gmail…
Source: https://n8n.io/workflows/17236/ — 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.
Openai Workflow. Uses openAi, gmailTrigger, gmail, crypto. Event-driven trigger; 49 nodes.
Complete AI-powered sales system Automates lead capture, qualification, and follow-up from multiple channels. AI INTELLIGENCE:
This workflow watches a Notion CRM for leads ready to contact, checks Gmail to avoid duplicate outreach, then sends or drafts an email with Google Drive PDF attachments and updates the lead status in
An automated quote generation system that monitors your inbox, classifies quote requests using AI, calculates intelligent pricing based on historical data, and provides a professional dashboard for re
LeadInboxTriageBot_GT. Uses gmailTrigger, openAi, googleSheets, gmail. Event-driven trigger; 36 nodes.