This workflow follows the Chainllm → Documentdefaultdataloader 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 →
{
"name": "Draft grounded support replies from a Notion knowledge base using Groq and Gmail",
"nodes": [
{
"id": "11d843a0-a945-4bcb-b8af-ec94d3c97c77",
"name": "Run KB Ingestion",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-256,
288
],
"parameters": {}
},
{
"id": "5c71ae62-9387-4141-afc8-d31ef3f6e9c6",
"name": "Get KB Articles",
"type": "n8n-nodes-base.notion",
"typeVersion": 2.2,
"position": [
-64,
288
],
"parameters": {
"resource": "databasePage",
"operation": "getAll",
"databaseId": {
"__rl": true,
"mode": "list",
"value": "YOUR_NOTION_DATABASE_ID",
"cachedResultName": "Cadence Knowledge Base"
},
"returnAll": true,
"options": {}
},
"onError": "continueRegularOutput"
},
{
"id": "b4bfbbe8-2deb-481b-b33a-d3bf7eb3ca9a",
"name": "Check If Published",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
144,
288
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"leftValue": "={{ $json.property_status }}",
"operator": {
"type": "string",
"operation": "equals"
},
"rightValue": "Published"
}
],
"combinator": "and"
}
}
},
{
"id": "95babe45-8bbd-4238-8628-09c21c4c49f7",
"name": "Get Article Body",
"type": "n8n-nodes-base.notion",
"typeVersion": 2.2,
"position": [
464,
272
],
"parameters": {
"resource": "block",
"operation": "getAll",
"blockId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.id }}"
},
"returnAll": true
},
"onError": "continueRegularOutput"
},
{
"id": "ed62031f-a67e-48d2-804a-111bdcef98b9",
"name": "Assemble Article Documents",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
640,
272
],
"parameters": {
"jsCode": "const pages = $('Get KB Articles').all().map((i) => i.json);\nconst norm = (s) => (s || '').toString().replace(/-/g, '');\nconst byId = {};\nfor (const p of pages) {\n byId[norm(p.id)] = { title: p.property_title || p.name || 'Untitled', category: p.property_category || '', url: p.url || '', lastUpdated: (p.property_last_updated && p.property_last_updated.start) || '', parts: [] };\n}\nfor (const b of $input.all()) {\n const j = b.json;\n const pid = norm(j.root_id || j.parent_id || (j.parent && j.parent.page_id) || '');\n const c = (j.content || '').trim();\n if (byId[pid] && c) byId[pid].parts.push(c);\n}\nreturn Object.values(byId)\n .filter((a) => a.parts.length > 0)\n .map((a) => ({ json: { title: a.title, category: a.category, url: a.url, last_updated: a.lastUpdated, text: a.parts.join('\\n\\n') } }));"
}
},
{
"id": "51924453-ea25-44fa-9b7b-c26e79f027fd",
"name": "Store KB Vectors",
"type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
"typeVersion": 1.3,
"position": [
1024,
224
],
"parameters": {
"mode": "insert",
"memoryKey": {
"__rl": true,
"mode": "list",
"value": "cadence_kb",
"cachedResultName": "cadence_kb"
},
"clearStore": true
}
},
{
"id": "259251b9-ce90-438c-8612-78d98e25353f",
"name": "Load Article Text",
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [
1088,
400
],
"parameters": {
"dataType": "json",
"jsonMode": "expressionData",
"jsonData": "={{ $json.text }}",
"textSplittingMode": "custom",
"options": {
"metadata": {
"metadataValues": [
{
"name": "title",
"value": "={{ $json.title }}"
},
{
"name": "category",
"value": "={{ $json.category }}"
},
{
"name": "url",
"value": "={{ $json.url }}"
},
{
"name": "last_updated",
"value": "={{ $json.last_updated }}"
},
{
"name": "indexed_at",
"value": "={{ $now.toISO() }}"
}
]
}
}
}
},
{
"id": "83fb4f82-8af9-40cf-a250-458032c9f0fd",
"name": "Split Article Into Chunks",
"type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
"typeVersion": 1,
"position": [
1088,
544
],
"parameters": {
"chunkSize": 1000,
"chunkOverlap": 100,
"options": {
"splitCode": "markdown"
}
}
},
{
"id": "bafebe06-a755-4eec-bfb8-a4c09cf7bba4",
"name": "When Support Email Arrives",
"type": "n8n-nodes-base.gmailTrigger",
"typeVersion": 1.4,
"position": [
-224,
1056
],
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"simple": false,
"filters": {
"readStatus": "unread",
"q": "to:support@yourcompany.com"
},
"options": {}
}
},
{
"id": "823effca-64d6-4967-b941-a901ba55841b",
"name": "Normalize Email",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-16,
1056
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "a1",
"name": "emailSubject",
"value": "={{ $json.subject }}",
"type": "string"
},
{
"id": "a2",
"name": "emailBody",
"value": "={{ $json.text ?? $json.snippet ?? \"\" }}",
"type": "string"
},
{
"id": "a3",
"name": "senderEmail",
"value": "={{ $json.from }}",
"type": "string"
},
{
"id": "a4",
"name": "threadId",
"value": "={{ $json.threadId }}",
"type": "string"
},
{
"id": "a5",
"name": "question",
"value": "={{ $json.subject }}\n\n{{ $json.text }}",
"type": "string"
},
{
"id": "a6",
"name": "companyName",
"value": "Cadence",
"type": "string"
},
{
"id": "a7",
"name": "signature",
"value": "Cadence Support",
"type": "string"
},
{
"id": "a8",
"name": "minRelevance",
"value": 0.3,
"type": "number"
}
]
},
"options": {}
}
},
{
"id": "cfcae77d-93e4-4a75-9e47-635fcef15db8",
"name": "Classify Inquiry",
"type": "@n8n/n8n-nodes-langchain.textClassifier",
"typeVersion": 1.1,
"position": [
256,
1024
],
"parameters": {
"inputText": "={{ $json.question }}",
"categories": {
"categories": [
{
"category": "support_question",
"description": "A genuine product or account question that the knowledge base could answer."
},
{
"category": "noise",
"description": "Spam, newsletters, auto-replies, out-of-office, or messages that contain no question."
},
{
"category": "already_resolved",
"description": "A thank-you, confirmation, or message saying the issue is already solved and needs no answer."
}
]
},
"options": {
"fallback": "other"
}
}
},
{
"id": "69dd2cac-0c52-4d58-93a8-31fa6461f4d4",
"name": "Retrieve From KB",
"type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
"typeVersion": 1.3,
"position": [
752,
976
],
"parameters": {
"mode": "load",
"memoryKey": {
"__rl": true,
"mode": "list",
"value": "cadence_kb",
"cachedResultName": "cadence_kb"
},
"prompt": "={{ $('Normalize Email').item.json.question }}",
"topK": 8,
"includeDocumentMetadata": true,
"useReranker": true
}
},
{
"id": "e13981a1-36bd-4496-8a98-8d5dab88dfef",
"name": "Bundle Retrieved Sources",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1040,
976
],
"parameters": {
"jsCode": "const docs = $input.all().map((i) => i.json);\nconst norm = $('Normalize Email').first().json;\nconst minRel = Number(norm.minRelevance);\nconst md = (d) => d.metadata || (d.document && d.document.metadata) || {};\nconst content = (d) => d.pageContent || (d.document && d.document.pageContent) || d.text || '';\nconst scoreOf = (d) => {\n const m = md(d);\n const cands = [d.relevanceScore, d.rerankScore, d.score, m.relevanceScore, m.rerankScore, m.score];\n const n = cands.find((v) => typeof v === 'number');\n return typeof n === 'number' ? n : null;\n};\nconst scores = docs.map(scoreOf).filter((v) => v !== null);\nconst topScore = scores.length ? Math.max.apply(null, scores) : null;\nconst retrievalConfident = (docs.length > 0) && (topScore === null || isNaN(minRel) || topScore >= minRel);\nconst sourcesText = docs.map((d, i) => '[' + (i + 1) + '] ' + (md(d).title || 'Source') + '\\n' + content(d)).join('\\n\\n');\nconst seen = new Set();\nconst list = [];\nfor (const d of docs) {\n const t = md(d).title || 'Source';\n const u = md(d).url || '';\n if (!seen.has(t)) { seen.add(t); list.push('- ' + t + (u ? ' (' + u + ')' : '')); }\n}\nreturn [{ json: { sourcesText: sourcesText, sourcesList: list.join('\\n'), topScore: topScore, retrievalConfident: retrievalConfident, question: norm.question, emailSubject: norm.emailSubject, senderEmail: norm.senderEmail, threadId: norm.threadId } }];"
}
},
{
"id": "268acdb6-d236-4aa9-a195-2c14908d033d",
"name": "Check Retrieval Confidence",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1280,
976
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"leftValue": "={{ $json.retrievalConfident }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
}
}
},
{
"id": "a9f0d27f-3a99-4571-8c0b-b1de269ee12d",
"name": "Draft Grounded Reply",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"typeVersion": 1.9,
"position": [
1488,
960
],
"parameters": {
"promptType": "define",
"text": "=You are a customer support agent for Cadence, a team time-tracking and invoicing SaaS. Answer the customer question using ONLY the information in the sources below. Do not use any outside knowledge. If the sources do not contain enough information to answer, reply with exactly: NEEDS_HUMAN\n\nWrite a friendly, concise reply in plain text. Do not add a greeting line or a signature.\n\nSOURCES:\n{{ $json.sourcesText }}\n\nCUSTOMER QUESTION:\n{{ $json.question }}"
}
},
{
"id": "6a1181c8-ac7b-4f52-8651-ba645ec88ffb",
"name": "Check If Answered",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1760,
960
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"leftValue": "={{ $json.text }}",
"operator": {
"type": "string",
"operation": "notContains"
},
"rightValue": "NEEDS_HUMAN"
}
],
"combinator": "and"
}
}
},
{
"id": "231dce17-957b-49e5-898b-fbf7f6b9c899",
"name": "Build Reply With Sources",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2096,
944
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "b1",
"name": "replyBody",
"value": "={{ $('Draft Grounded Reply').item.json.text }}\n\nSources:\n{{ $('Bundle Retrieved Sources').item.json.sourcesList }}",
"type": "string"
},
{
"id": "b2",
"name": "senderEmail",
"value": "={{ $('Bundle Retrieved Sources').item.json.senderEmail }}",
"type": "string"
},
{
"id": "b3",
"name": "threadId",
"value": "={{ $('Bundle Retrieved Sources').item.json.threadId }}",
"type": "string"
},
{
"id": "b4",
"name": "emailSubject",
"value": "={{ $('Bundle Retrieved Sources').item.json.emailSubject }}",
"type": "string"
}
]
},
"options": {}
}
},
{
"id": "e05078d8-19fc-47ff-a696-6957e42cb7da",
"name": "Save Draft For Review",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
2304,
944
],
"parameters": {
"resource": "draft",
"operation": "create",
"subject": "=Re: {{ $json.emailSubject }}",
"emailType": "text",
"message": "={{ $json.replyBody }}",
"options": {
"sendTo": "={{ $json.senderEmail }}",
"threadId": "={{ $json.threadId }}"
}
}
},
{
"id": "52d06bee-c98a-4a4d-b28e-02cc4cbd21b1",
"name": "Groq Triage Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"typeVersion": 1,
"position": [
256,
1296
],
"parameters": {
"model": "llama-3.3-70b-versatile",
"options": {
"temperature": 0
}
}
},
{
"id": "34f958e2-6da4-49af-a6f9-38fe5ec33c2f",
"name": "Groq Draft Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"typeVersion": 1,
"position": [
1488,
1152
],
"parameters": {
"model": "llama-3.3-70b-versatile",
"options": {
"temperature": 0.2
}
}
},
{
"id": "47cf81a6-e566-4f0f-b890-29d4cc74eda4",
"name": "Embed Articles With Cohere",
"type": "@n8n/n8n-nodes-langchain.embeddingsCohere",
"typeVersion": 1,
"position": [
944,
400
],
"parameters": {
"modelName": "embed-english-v3.0"
}
},
{
"id": "74bd073e-af79-4649-96a8-b95b46a17702",
"name": "Embed Query With Cohere",
"type": "@n8n/n8n-nodes-langchain.embeddingsCohere",
"typeVersion": 1,
"position": [
752,
1200
],
"parameters": {
"modelName": "embed-english-v3.0"
}
},
{
"id": "8380e5c0-36fe-484f-994d-744bbb12fa40",
"name": "Rerank Matches With Cohere",
"type": "@n8n/n8n-nodes-langchain.rerankerCohere",
"typeVersion": 1,
"position": [
912,
1200
],
"parameters": {
"modelName": "rerank-v3.5",
"topN": 4
}
},
{
"id": "5e432721-723c-4f76-9300-9e6399acccb3",
"name": "Sticky Note - Overview",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-816,
192
],
"parameters": {
"content": "## Knowledge Base Inquiry Assistant (RAG)\n\n### How it works\n\n1. Run the ingestion trigger to read published articles from a Notion knowledge base, split each one into chunks, embed them with Cohere, and store the vectors in the built-in Simple Vector Store.\n2. When a support email arrives, a Groq classifier sorts it into a real question, noise, or an already-resolved thread, and only real questions continue.\n3. The question retrieves candidate chunks and a Cohere reranker reorders them by relevance, then a confidence gate stops weak matches before any model call.\n4. Groq drafts a reply using only the retrieved sources, and answers not found in the knowledge base are held back for a human.\n5. The grounded reply is saved as a Gmail draft with a sources list for a person to review and send.\n\n### Setup steps\n\n- [ ] Connect Notion, Groq, Cohere, and Gmail credentials in the credential store.\n- [ ] Point Get KB Articles at your own knowledge base database and set the Gmail trigger search query.\n- [ ] Run the ingestion trigger once to build the vector store, then activate the workflow.\n\n### Customization\n\nSwap the Groq model, tune topK and the reranker topN, or adjust minRelevance in Normalize Email to control how strict the confidence gate is. The Simple Vector Store is in-memory, so re-run ingestion after an n8n restart.",
"width": 480,
"height": 868
}
},
{
"id": "f215e47c-0eb0-4acf-8eaa-69a756809411",
"name": "Sticky Note - Read Articles",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-304,
160
],
"parameters": {
"content": "## Read published KB articles\nPull published pages from the Notion knowledge base. Draft articles are skipped.",
"color": 7,
"width": 664,
"height": 304
}
},
{
"id": "f9f040cc-b474-4045-a716-735e647471ad",
"name": "Sticky Note - Assemble Text",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
400,
112
],
"parameters": {
"content": "## Fetch and assemble article text\nRead each page body and join the blocks into one document per article.",
"color": 7,
"width": 420,
"height": 320
}
},
{
"id": "561a578b-857a-448c-8dd6-8f1860cd0599",
"name": "Sticky Note - Embed Store",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
864,
48
],
"parameters": {
"content": "## Embed and store KB vectors\nChunk the text, embed it with Cohere, and store the vectors in the Simple Vector Store. Each chunk keeps title, category, url, and last_updated metadata.",
"color": 7,
"width": 498,
"height": 652
}
},
{
"id": "814999c2-244e-47dd-85de-e1a49a5b87ce",
"name": "Sticky Note - Receive Email",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-288,
880
],
"parameters": {
"content": "## Receive and normalize email\nCapture each new support email and pull out the question, sender, and thread.",
"color": 7,
"width": 424,
"height": 368
}
},
{
"id": "93ae9837-6ad6-47ce-afe1-3e3d0cd7a47d",
"name": "Sticky Note - Triage",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
192,
848
],
"parameters": {
"content": "## Triage the inquiry\nDrop noise and already-resolved threads so only real questions continue.",
"color": 7,
"width": 412,
"height": 572
}
},
{
"id": "4758ac0f-9037-4b6f-90ab-9c7862844bd9",
"name": "Sticky Note - Retrieve Rerank",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
656,
800
],
"parameters": {
"content": "## Retrieve and rerank sources\nFetch candidate chunks for the question and rerank them by relevance with Cohere, then bundle a sources list.",
"color": 7,
"width": 544,
"height": 556
}
},
{
"id": "7ac4fb18-c5f3-4155-a53d-43374146847a",
"name": "Sticky Note - Gate Draft",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1248,
784
],
"parameters": {
"content": "## Gate and draft the reply\nStop weak retrievals, then draft a reply from the sources only. Answers not found in the knowledge base are held back for a human.",
"color": 7,
"width": 721,
"height": 504
}
},
{
"id": "210a31f9-ff36-4235-a46e-6666179be2d8",
"name": "Sticky Note - Save Draft",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
2016,
768
],
"parameters": {
"content": "## Build and save the draft\nSave a Gmail draft with sources for a person to review. Nothing is auto-sent.",
"color": 7,
"width": 484,
"height": 364
}
}
],
"connections": {
"Run KB Ingestion": {
"main": [
[
{
"node": "Get KB Articles",
"type": "main",
"index": 0
}
]
]
},
"Get KB Articles": {
"main": [
[
{
"node": "Check If Published",
"type": "main",
"index": 0
}
]
]
},
"Check If Published": {
"main": [
[
{
"node": "Get Article Body",
"type": "main",
"index": 0
}
],
[]
]
},
"Get Article Body": {
"main": [
[
{
"node": "Assemble Article Documents",
"type": "main",
"index": 0
}
]
]
},
"Assemble Article Documents": {
"main": [
[
{
"node": "Store KB Vectors",
"type": "main",
"index": 0
}
]
]
},
"Embed Articles With Cohere": {
"ai_embedding": [
[
{
"node": "Store KB Vectors",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Load Article Text": {
"ai_document": [
[
{
"node": "Store KB Vectors",
"type": "ai_document",
"index": 0
}
]
]
},
"Split Article Into Chunks": {
"ai_textSplitter": [
[
{
"node": "Load Article Text",
"type": "ai_textSplitter",
"index": 0
}
]
]
},
"When Support Email Arrives": {
"main": [
[
{
"node": "Normalize Email",
"type": "main",
"index": 0
}
]
]
},
"Normalize Email": {
"main": [
[
{
"node": "Classify Inquiry",
"type": "main",
"index": 0
}
]
]
},
"Classify Inquiry": {
"main": [
[
{
"node": "Retrieve From KB",
"type": "main",
"index": 0
}
],
[],
[],
[]
]
},
"Groq Triage Model": {
"ai_languageModel": [
[
{
"node": "Classify Inquiry",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Retrieve From KB": {
"main": [
[
{
"node": "Bundle Retrieved Sources",
"type": "main",
"index": 0
}
]
]
},
"Embed Query With Cohere": {
"ai_embedding": [
[
{
"node": "Retrieve From KB",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Rerank Matches With Cohere": {
"ai_reranker": [
[
{
"node": "Retrieve From KB",
"type": "ai_reranker",
"index": 0
}
]
]
},
"Bundle Retrieved Sources": {
"main": [
[
{
"node": "Check Retrieval Confidence",
"type": "main",
"index": 0
}
]
]
},
"Check Retrieval Confidence": {
"main": [
[
{
"node": "Draft Grounded Reply",
"type": "main",
"index": 0
}
],
[]
]
},
"Draft Grounded Reply": {
"main": [
[
{
"node": "Check If Answered",
"type": "main",
"index": 0
}
]
]
},
"Groq Draft Model": {
"ai_languageModel": [
[
{
"node": "Draft Grounded Reply",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Check If Answered": {
"main": [
[
{
"node": "Build Reply With Sources",
"type": "main",
"index": 0
}
],
[]
]
},
"Build Reply With Sources": {
"main": [
[
{
"node": "Save Draft For Review",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"meta": {
"templateCredsSetupCompleted": true
},
"tags": []
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Draft grounded support replies from a Notion knowledge base using Groq and Gmail. Uses notion, vectorStoreInMemory, documentDefaultDataLoader, textSplitterRecursiveCharacterTextSplitter. Event-driven trigger; 32 nodes.
Source: https://github.com/exekyute/n8n-exekyute-templates/blob/main/published/n8n-kb-inquiry-assistant/workflow.json — 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 workflow ingests onboarding documents from Google Drive into an in-memory vector store with Google Gemini embeddings, then runs a multi-channel onboarding chatbot powered by Groq, sends role-base
This workflow is built for individuals, teams, and businesses that receive regular inquiries via email and want to automate responses in a way that’s intelligent, brand-aligned, and always up to date.
This workflow ingests published Notion knowledge base articles into an in-memory vector store using Cohere embeddings, then monitors a support Gmail inbox and uses Groq to draft grounded reply emails
n8n-4-1: Qdrant. Uses vectorStoreQdrant, embeddingsOpenAi, textClassifier, chainSummarization. Event-driven trigger; 27 nodes.
Alfred (funcional). Uses gmailTool, googleCalendarTool, gmail, embeddingsOpenAi. Event-driven trigger; 83 nodes.