This workflow corresponds to n8n.io template #11807 — we link there as the canonical source.
This workflow follows the Agent → Chainllm 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": "rIznyhWPvQUhX7jP",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Enhance Customer Support with RAG-Powered AI",
"tags": [
{
"id": "BozSnIOP0arZwOpu",
"name": "tuguidragos.com",
"createdAt": "2025-12-14T19:49:53.386Z",
"updatedAt": "2025-12-14T19:49:53.386Z"
}
],
"nodes": [
{
"id": "ec6e8de4-fafd-4cef-bb57-df337cac3063",
"name": "Workflow Configuration",
"type": "n8n-nodes-base.set",
"position": [
-3440,
-11584
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "confidenceThreshold",
"type": "number",
"value": 0.7
},
{
"id": "id-2",
"name": "supabaseTable",
"type": "string",
"value": "={{ $env.SUPABASE_TABLE || \"support_docs\" }}"
},
{
"id": "id-3",
"name": "maxRetrievalDocs",
"type": "number",
"value": 5
},
{
"id": "id-4",
"name": "slackChannelId",
"type": "string",
"value": "={{ $env.SLACK_CHANNEL_ID }}"
},
{
"id": "id-5",
"name": "slackSupportChannelId",
"type": "string",
"value": "={{ $env.SLACK_SUPPORT_CHANNEL_ID }}"
},
{
"id": "id-6",
"name": "slackWeeklySummaryChannelId",
"type": "string",
"value": "={{ $env.SLACK_WEEKLY_SUMMARY_CHANNEL_ID }}"
},
{
"id": "id-7",
"name": "supportEmail",
"type": "string",
"value": "={{ $env.SUPPORT_EMAIL }}"
},
{
"id": "id-8",
"name": "openaiModel",
"type": "string",
"value": "={{ $env.OPENAI_MODEL || \"gpt-4o-mini\" }}"
},
{
"id": "id-9",
"name": "embeddingModel",
"type": "string",
"value": "={{ $env.EMBEDDING_MODEL || \"text-embedding-3-small\" }}"
},
{
"id": "id-10",
"name": "googleSheetsDocId",
"type": "string",
"value": "={{ $env.GOOGLE_SHEETS_DOC_ID }}"
},
{
"id": "id-11",
"name": "googleSheetsLogSheet",
"type": "string",
"value": "={{ $env.GOOGLE_SHEETS_LOG_SHEET || \"ConversationLogs\" }}"
},
{
"id": "id-12",
"name": "googleSheetsMetricsSheet",
"type": "string",
"value": "={{ $env.GOOGLE_SHEETS_METRICS_SHEET || \"Metrics\" }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "430fea0c-deff-4198-b483-3191fcd10bac",
"name": "Email Trigger (IMAP)",
"type": "n8n-nodes-base.emailReadImap",
"position": [
-3856,
-11968
],
"parameters": {
"options": {}
},
"credentials": {
"imap": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "66725d8b-b8d8-4740-8017-8caf657c5566",
"name": "Webhook Trigger (Live Chat)",
"type": "n8n-nodes-base.webhook",
"position": [
-3856,
-11776
],
"parameters": {
"path": "6a859a34-1365-4f04-bf0e-024fd18f0458",
"options": {}
},
"typeVersion": 2.1
},
{
"id": "484c3911-cfe2-4753-a123-2e08e809d803",
"name": "WhatsApp Trigger",
"type": "n8n-nodes-base.whatsAppTrigger",
"position": [
-3856,
-11584
],
"parameters": {
"options": {},
"updates": [
"account_review_update"
]
},
"credentials": {
"whatsAppTriggerApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "e0381599-3896-4c1e-b30f-07afe5d79a52",
"name": "Slack Trigger (Mentions)",
"type": "n8n-nodes-base.slackTrigger",
"position": [
-3856,
-11392
],
"parameters": {
"options": {},
"trigger": [
"app_mention"
],
"channelId": {
"__rl": true,
"mode": "id",
"value": "={{ $env.SLACK_CHANNEL_ID }}"
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "1e3bd0bc-856a-4040-97de-226b82d8b565",
"name": "Discord Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"position": [
-3856,
-11200
],
"parameters": {
"path": "039f8d52-8b2b-4769-aecb-c8957f5261ff",
"options": {}
},
"typeVersion": 2.1
},
{
"id": "3d109a1f-77ba-412f-a47f-28358986b006",
"name": "Normalize Payload",
"type": "n8n-nodes-base.set",
"position": [
-3184,
-11584
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "channel",
"type": "string",
"value": "={{ $json.from ? 'email' : ($json.body?.query ? 'webhook' : ($json.message ? 'whatsapp' : ($json.event?.type ? 'slack' : ($json.content ? 'discord' : 'other')))) }}"
},
{
"id": "id-2",
"name": "userId",
"type": "string",
"value": "={{ $json.from || $json.user_id || $json.sender || $json.event?.user || $json.author?.id }}"
},
{
"id": "id-3",
"name": "message",
"type": "string",
"value": "={{ $json.text || $json.body?.query || $json.message || $json.content || $json.event?.text }}"
},
{
"id": "id-4",
"name": "timestamp",
"type": "string",
"value": "={{ $now }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "dfd4d82d-7136-4d9f-bce7-438c8057bfd4",
"name": "Preprocess Query",
"type": "n8n-nodes-base.code",
"position": [
-2992,
-11184
],
"parameters": {
"jsCode": "const message = $input.item.json.message || '';\nconst cleaned = message.trim().toLowerCase();\nconst keywords = cleaned.split(' ').filter(w => w.length > 3);\n\nreturn {\n json: {\n ...$input.item.json,\n cleanedQuery: cleaned,\n keywords,\n chatInput: message\n }\n};"
},
"typeVersion": 2
},
{
"id": "737a31af-cf3c-4115-8e90-66620f60a0e6",
"name": "OpenAI Chat Model (RAG)",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-2560,
-10960
],
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.openaiModel }}"
},
"options": {
"temperature": 0.7
},
"builtInTools": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "7962165b-d30c-4bef-ac99-3fd4009e8aeb",
"name": "OpenAI Chat Model (Sentiment)",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-1744,
-10960
],
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.openaiModel }}"
},
"options": {
"temperature": 0.3
},
"builtInTools": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "d335f921-fd44-465b-8fd1-e7912eea8c50",
"name": "OpenAI Chat Model (Escalation)",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-912,
-10576
],
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.openaiModel }}"
},
"options": {
"temperature": 0.5
},
"builtInTools": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "9d919833-cb8f-4283-84ae-6f28a2e80c2d",
"name": "Sentiment Analysis",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
-2016,
-11184
],
"parameters": {
"text": "={{ $json.message }}",
"batching": {},
"messages": {
"messageValues": [
{
"message": "You are a sentiment analysis expert. Analyze the customer message and respond with ONLY one word: positive, neutral, or negative. Consider tone, urgency, and emotional content."
}
]
},
"promptType": "define"
},
"typeVersion": 1.7
},
{
"id": "bc133108-9da9-4b0d-bc89-fbe572fe8ca7",
"name": "Confidence Scoring",
"type": "n8n-nodes-base.code",
"position": [
-1696,
-11184
],
"parameters": {
"jsCode": "const response = $input.item.json.output || $input.item.json.text || '';\nconst sentiment = $input.item.json.sentiment || 'neutral';\n\n// Calculate confidence based on multiple factors\nconst hasReferences = response.includes('http') || response.includes('documentation');\nconst responseLength = response.length;\nconst isSpecific = responseLength > 100 && (response.includes('step') || response.includes('follow') || response.includes('guide'));\n\n// Base confidence calculation\nlet confidence = 0.5;\n\n// Add points for references\nif (hasReferences) confidence += 0.2;\n\n// Add points for specificity\nif (isSpecific) confidence += 0.15;\n\n// Add points for response length (longer, more detailed responses)\nif (responseLength > 200) confidence += 0.1;\nelse if (responseLength > 100) confidence += 0.05;\n\n// Cap confidence at 0.95\nconfidence = Math.min(confidence, 0.95);\n\nreturn {\n json: {\n ...$input.item.json,\n confidence,\n aiResponse: response,\n sentiment\n }\n};"
},
"typeVersion": 2
},
{
"id": "e30a406d-8e4c-4002-bf52-15d4ca11dde5",
"name": "Check Confidence Threshold",
"type": "n8n-nodes-base.if",
"position": [
-1472,
-11184
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.confidence }}",
"rightValue": "0.7"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "764829e3-e9de-41b3-8b03-273db8228906",
"name": "Check Sentiment",
"type": "n8n-nodes-base.if",
"position": [
-1200,
-11456
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $json.sentiment }}",
"rightValue": "negative"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "695a3e3d-97b2-49db-9b98-e86f6f82d0e9",
"name": "Escalation Needed?",
"type": "n8n-nodes-base.if",
"position": [
-1200,
-10736
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "or",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "number",
"operation": "lt"
},
"leftValue": "={{ $json.confidence }}",
"rightValue": "0.7"
},
{
"id": "id-2",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.sentiment }}",
"rightValue": "negative"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "4e8b9cc0-3948-45bb-92ad-6a2f170d7bee",
"name": "Business Hours Check",
"type": "n8n-nodes-base.if",
"position": [
-512,
-10752
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "boolean",
"operation": "true"
},
"leftValue": "={{ new Date().getHours() >= 9 && new Date().getHours() < 17 }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "7eaeac96-0539-4d3f-b14d-06843eb05f17",
"name": "Channel Router",
"type": "n8n-nodes-base.switch",
"position": [
-288,
-11552
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Email",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition-1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.channel }}",
"rightValue": "email"
}
]
},
"renameOutput": true
},
{
"outputKey": "Webhook",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition-2",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.channel }}",
"rightValue": "webhook"
}
]
},
"renameOutput": true
},
{
"outputKey": "WhatsApp",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition-3",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.channel }}",
"rightValue": "whatsapp"
}
]
},
"renameOutput": true
},
{
"outputKey": "Discord",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition-4",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.channel }}",
"rightValue": "discord"
}
]
},
"renameOutput": true
},
{
"outputKey": "Slack",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition-5",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.channel }}",
"rightValue": "slack"
}
]
},
"renameOutput": true
}
]
},
"options": {}
},
"typeVersion": 3.3
},
{
"id": "4232df5a-fbd4-44c5-a09b-7a353c75afce",
"name": "Format Email Response",
"type": "n8n-nodes-base.set",
"position": [
16,
-11888
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "emailBody",
"type": "string",
"value": "={{ $json.aiResponse }}"
},
{
"id": "id-2",
"name": "emailSubject",
"type": "string",
"value": "Re: Your Support Request"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "a4470db0-d8d0-43c0-85d2-c8df84c912ef",
"name": "Send Email Reply",
"type": "n8n-nodes-base.emailSend",
"position": [
336,
-11888
],
"parameters": {
"html": "={{ $json.emailBody }}",
"options": {},
"subject": "={{ $json.emailSubject }}",
"toEmail": "={{ $json.userId }}",
"fromEmail": "={{ $('Workflow Configuration').first().json.supportEmail }}"
},
"credentials": {
"smtp": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "2e911984-ac9a-43dd-a0ea-02cd814c4006",
"name": "Escalation Reasoning",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
-992,
-10752
],
"parameters": {
"text": "={{ 'Message: ' + $json.message + '\\nAI Response: ' + $json.aiResponse + '\\nConfidence: ' + $json.confidence + '\\nSentiment: ' + $json.sentiment }}",
"batching": {},
"messages": {
"messageValues": [
{
"message": "You are an escalation analyst. Review the customer interaction and explain why this requires human intervention. Be concise and specific about the escalation reason."
}
]
},
"promptType": "define"
},
"typeVersion": 1.7
},
{
"id": "9a32a50d-4907-4bd0-803d-d5af6d5d2fec",
"name": "Prepare Ticket Object",
"type": "n8n-nodes-base.set",
"position": [
-688,
-10752
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "ticketSubject",
"type": "string",
"value": "={{ 'Escalation: ' + $json.message.substring(0, 50) }}"
},
{
"id": "id-2",
"name": "ticketDescription",
"type": "string",
"value": "={{ 'User: ' + $json.userId + '\\nMessage: ' + $json.message + '\\nAI Response: ' + $json.aiResponse + '\\nConfidence: ' + $json.confidence }}"
},
{
"id": "id-3",
"name": "priority",
"type": "string",
"value": "={{ $json.sentiment === 'negative' ? 'high' : 'normal' }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "1c332dc1-a8d1-4f99-b748-f49dcc5a9f8d",
"name": "Create Zendesk Ticket",
"type": "n8n-nodes-base.zendesk",
"position": [
-336,
-10832
],
"parameters": {
"description": "={{ $json.ticketDescription }}",
"additionalFields": {
"subject": "={{ $json.ticketSubject }}"
}
},
"credentials": {
"zendeskApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "dfb0f025-3147-4ac9-96c0-9a5ce6086cae",
"name": "Update Zendesk Ticket",
"type": "n8n-nodes-base.zendesk",
"position": [
-336,
-10656
],
"parameters": {
"id": "={{ $json.ticketId }}",
"operation": "update",
"updateFields": {
"internalNote": "={{ $json.ticketDescription }}"
}
},
"credentials": {
"zendeskApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "2dde2c80-830d-49a8-ac89-72722d703ba0",
"name": "Escalation Alert to Support Team",
"type": "n8n-nodes-base.slack",
"position": [
-160,
-10752
],
"parameters": {
"text": "={{ '\ud83d\udea8 Escalation Alert\nUser: ' + $json.userId + '\nPriority: ' + $json.priority + '\nReason: ' + ($json.confidence < 0.7 ? 'Low confidence' : 'Negative sentiment') + '\nMessage: ' + $json.message }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.slackSupportChannelId }}"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "659c8db9-269b-478e-b34e-b4949800ee9f",
"name": "Auto-Categorization",
"type": "n8n-nodes-base.code",
"position": [
528,
-11888
],
"parameters": {
"jsCode": "const raw = $input.item.json.message ?? '';\nconst message = String(raw).toLowerCase();\n\nlet category = 'general';\n\nif (/\\b(billing|payment)\\b/.test(message)) category = 'billing';\nelse if (/\\b(bug|error)\\b/.test(message)) category = 'technical';\nelse if (/\\b(account|login)\\b/.test(message)) category = 'account';\nelse if (/\\bintegration\\b/.test(message)) category = 'integration';\n\nreturn { json: { ...$input.item.json, category } };\n"
},
"typeVersion": 2
},
{
"id": "dfcb156f-79c8-417e-80b7-c7c51dd8afce",
"name": "Log Conversation to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
720,
-11888
],
"parameters": {
"columns": {
"value": {
"userId": "={{ $json.userId }}",
"channel": "={{ $json.channel }}",
"message": "={{ $json.message }}",
"category": "={{ $json.category }}",
"sentiment": "={{ $json.sentiment }}",
"timestamp": "={{ $json.timestamp }}",
"aiResponse": "={{ $json.aiResponse }}",
"confidence": "={{ $json.confidence }}"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Workflow Configuration').first().json.googleSheetsLogSheet }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.googleSheetsDocId }}"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "d688bdf8-ca16-4c49-b97d-a48ac0b5b19e",
"name": "Weekly Maintenance Schedule",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-2592,
-10576
],
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"typeVersion": 1.3
},
{
"id": "b60e07af-f9ae-4aa3-862b-c5157794f815",
"name": "Fetch Weekly Metrics",
"type": "n8n-nodes-base.googleSheets",
"position": [
-2416,
-10576
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Workflow Configuration').first().json.googleSheetsMetricsSheet }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.googleSheetsDocId }}"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "e771b416-8588-42ad-8407-2893367c064f",
"name": "Supabase Vector Insert",
"type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
"position": [
-1856,
-10576
],
"parameters": {
"mode": "insert",
"options": {},
"tableName": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.supabaseTable }}"
}
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "a9e93c93-80ea-410e-8c02-420c48e1cc81",
"name": "OpenAI Embeddings (Insert)",
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"position": [
-2016,
-10304
],
"parameters": {
"model": "={{ $('Workflow Configuration').first().json.embeddingModel }}",
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "e81a2997-c287-41f7-98d4-22470f627721",
"name": "Chat Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"position": [
-2384,
-10960
],
"parameters": {},
"typeVersion": 1.3
},
{
"id": "a7e9af55-958a-4272-9b98-b15d087cea4d",
"name": "Conversation History Manager",
"type": "n8n-nodes-base.code",
"position": [
-2992,
-11584
],
"parameters": {
"jsCode": "const sessionId = $input.item.json.userId ?? 'default';\n\nconst conversationHistory = Array.isArray($input.item.json.conversationHistory)\n ? $input.item.json.conversationHistory\n : [];\n\nreturn {\n json: {\n ...$input.item.json,\n sessionId,\n conversationHistory,\n isNewConversation: conversationHistory.length === 0\n }\n};\n"
},
"typeVersion": 2
},
{
"id": "bbe03d5f-ee07-4f51-9016-5ce591389667",
"name": "New vs Existing Conversation",
"type": "n8n-nodes-base.if",
"position": [
-2816,
-11584
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.isNewConversation }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "f6bcb3bc-d20a-42b2-8e86-a6140c16425c",
"name": "Human Takeover Detection",
"type": "n8n-nodes-base.if",
"position": [
-528,
-11488
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "string",
"operation": "notContains"
},
"leftValue": "={{ $json.message }}",
"rightValue": "agent"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "7c19fcdd-4dfe-4a1d-9f3a-6846d29d97ea",
"name": "Log Conversation to Zendesk",
"type": "n8n-nodes-base.zendesk",
"position": [
912,
-11888
],
"parameters": {
"description": "={{ 'Conversation log: ' + $json.category + ' - ' + $json.message.substring(0, 100) }}",
"additionalFields": {
"subject": "={{ 'Log: ' + $json.channel + ' - ' + $json.userId }}"
}
},
"credentials": {
"zendeskApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "8e1ab4da-3c5b-4044-a60d-476f3efeafb1",
"name": "Send Weekly Summary",
"type": "n8n-nodes-base.slack",
"position": [
-1504,
-10576
],
"parameters": {
"text": "={{ '\ud83d\udcca Weekly Support Summary\nTotal conversations: ' + $json.totalCount + '\nAvg confidence: ' + $json.avgConfidence + '\nEscalations: ' + $json.escalations }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.slackWeeklySummaryChannelId }}"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "e2a30a17-1331-4b90-ad6d-0b92dbc9102e",
"name": "Check Message Scope",
"type": "n8n-nodes-base.if",
"position": [
-848,
-11472
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "string",
"operation": "notContains"
},
"leftValue": "={{ $json.message }}",
"rightValue": "speak to human"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "17778047-31ad-48d6-b369-33b7e9d5cb9c",
"name": "Document Loader",
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"position": [
-1888,
-10304
],
"parameters": {
"options": {},
"textSplittingMode": "custom"
},
"typeVersion": 1.1
},
{
"id": "6c3ea672-7e9b-48a0-b2b7-0d9861b298f2",
"name": "Text Splitter",
"type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
"position": [
-1808,
-10096
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "0d3be44b-08f0-4833-90e2-b64a6fa218a1",
"name": "RAG Support Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-2400,
-11184
],
"parameters": {
"text": "={{ $json.chatInput || $json.message }}",
"options": {
"systemMessage": "You are a helpful customer support AI assistant. Use the available knowledge base to answer customer questions accurately. If you cannot find relevant information, acknowledge this and suggest escalation to a human agent. Always be professional, empathetic, and concise."
}
},
"typeVersion": 3
},
{
"id": "dfd9fd44-5e1c-45f5-a29f-be17e7b1147f",
"name": "Fetch Conversation History",
"type": "n8n-nodes-base.code",
"position": [
-2528,
-11792
],
"parameters": {
"jsCode": "// Fetch conversation history from memory store based on userId\nconst userId = $input.item.json.userId || 'default';\nconst sessionId = $input.item.json.sessionId || userId;\n\n// In a real implementation, this would query Supabase or a memory store\n// For now, we'll simulate fetching history from the workflow context\nconst conversationHistory = $input.item.json.conversationHistory || [];\n\n// Add current message to history\nconst currentMessage = {\n role: 'user',\n content: $input.item.json.message,\n timestamp: $input.item.json.timestamp || new Date().toISOString()\n};\n\nconst updatedHistory = [...conversationHistory, currentMessage];\n\n// Keep only last 10 messages to avoid context overflow\nconst recentHistory = updatedHistory.slice(-10);\n\nreturn {\n json: {\n ...$input.item.json,\n conversationHistory: recentHistory,\n historyCount: recentHistory.length,\n sessionId\n }\n};"
},
"typeVersion": 2
},
{
"id": "aaed828d-371c-4dd5-ace6-998adf526612",
"name": "Merge History with Query",
"type": "n8n-nodes-base.merge",
"position": [
-2528,
-11552
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3
},
{
"id": "827fc1fb-7ef7-4cc2-b848-316a9704e0d2",
"name": "Format Webhook Response",
"type": "n8n-nodes-base.set",
"position": [
16,
-11712
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "response",
"type": "string",
"value": "={{ $json.aiResponse }}"
},
{
"id": "id-2",
"name": "status",
"type": "string",
"value": "success"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ab13a033-f495-4208-89fa-02d552af5205",
"name": "Format WhatsApp Response",
"type": "n8n-nodes-base.set",
"position": [
16,
-11504
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "message",
"type": "string",
"value": "={{ $json.aiResponse }}"
},
{
"id": "id-2",
"name": "to",
"type": "string",
"value": "={{ $json.userId }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "8befe86f-9f11-4c03-8ed4-fab29d726ecf",
"name": "Format Discord Response",
"type": "n8n-nodes-base.set",
"position": [
16,
-11296
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "content",
"type": "string",
"value": "={{ $json.aiResponse }}"
},
{
"id": "id-2",
"name": "channel_id",
"type": "string",
"value": "={{ $json.channelId }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "227e2611-e88e-4069-83d9-283918e44cdc",
"name": "Format Slack Response",
"type": "n8n-nodes-base.set",
"position": [
16,
-11088
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "text",
"type": "string",
"value": "={{ $json.aiResponse }}"
},
{
"id": "id-2",
"name": "channel",
"type": "string",
"value": "={{ $json.event?.channel }}"
},
{
"id": "id-3",
"name": "thread_ts",
"type": "string",
"value": "={{ $json.event?.ts }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d995793d-2fb7-4b43-9dad-4efcae1de776",
"name": "End Workflow",
"type": "n8n-nodes-base.noOp",
"position": [
352,
-11504
],
"parameters": {},
"typeVersion": 1
},
{
"id": "a2d88b04-d76d-4b41-bcf6-4a4abb36c21c",
"name": "Prepare Metrics for Vector Insert",
"type": "n8n-nodes-base.set",
"position": [
-2128,
-10688
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "pageContent",
"type": "string",
"value": "=Weekly metrics summary: {{ JSON.stringify($json) }}"
},
{
"id": "id-2",
"name": "metadata",
"type": "object",
"value": "={ \"source\": \"weekly_metrics\", \"timestamp\": $now }"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "5097ea96-8338-42f9-9a1e-beb543445b0e",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2288,
-11968
],
"parameters": {
"width": 960,
"height": 592,
"content": "## How it works\nThis workflow is a multi-channel customer support AI system powered by RAG (Retrieval Augmented Generation). It ingests inbound messages from multiple sources (Email, Live Chat webhook, WhatsApp, Slack mentions, Discord webhook), normalizes them into a single schema, and manages per-user conversation continuity.\n\nFor each incoming message, it optionally loads recent conversation history, preprocesses the query, and routes it through a RAG Support Agent that generates an answer grounded in the product documentation. The workflow then performs sentiment classification and computes a confidence score for the generated response. Based on confidence and sentiment, it either sends an automated response back through the original channel, or escalates the case to human support.\n\nWhen escalation is needed, an AI escalation reasoning step produces a concise justification, a ticket object is prepared, business hours are checked, and a Zendesk ticket is created or updated. The support team is alerted in Slack with relevant context. For non-escalated cases, responses are formatted per channel, delivered, and the conversation is automatically categorized and logged to Google Sheets and Zendesk for audit and analytics.\n\nIn addition, a weekly maintenance job pulls metrics from Google Sheets, prepares them for vector storage, embeds and inserts them into Supabase (vector store) to improve retrieval quality over time, and posts a weekly summary in Slack.\n\n## Setup steps\n1. Populate environment variables used in **Workflow Configuration** (Slack channels, Sheets IDs, Supabase table, models, support email).\n2. Connect credentials for Email (IMAP + SMTP/send), WhatsApp, Slack, Zendesk, Google Sheets, Supabase, and OpenAI.\n3. Validate normalization fields across channels (userId/message/channel) and test both \u201cauto-response\u201d and \u201cescalation\u201d paths.\n4. Confirm the weekly vector insert pipeline (Document Loader/Text Splitter/Embeddings/Supabase) runs end-to-end."
},
"typeVersion": 1
},
{
"id": "d39ac675-03c7-44af-812e-3c5e6ff74ec5",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4016,
-12144
],
"parameters": {
"color": 7,
"width": 416,
"height": 1088,
"content": "## Inbound Triggers\nStarts the workflow from multiple sources (email, live chat webhook, WhatsApp, Slack mention, Discord webhook) and forwards all events into a shared processing pipeline."
},
"typeVersion": 1
},
{
"id": "f2952437-fc9d-4d19-bff6-dd4773f97d96",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3536,
-11728
],
"parameters": {
"color": 7,
"width": 288,
"height": 288,
"content": "## Global Configuration\nDefines system-wide settings: confidence threshold, model choices, vector store table, logging destinations, and Slack/support routing identifiers."
},
"typeVersion": 1
},
{
"id": "69880513-b52b-47df-bfed-37bb80130f80",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2608,
-11968
],
"parameters": {
"color": 7,
"width": 272,
"height": 592,
"content": "## Context Enrichment (History)\nFetches recent conversation history for the user and merges it with the current query to provide context for downstream reasoning."
},
"typeVersion": 1
},
{
"id": "88c9b99e-e955-4c1e-8af8-29e194e2e8e7",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3216,
-11728
],
"parameters": {
"color": 7,
"width": 528,
"height": 288,
"content": "## Normalize & Conversation State\nNormalizes payloads to a common schema (channel, userId, message, timestamp) and determines whether the message belongs to a new or existing conversation."
},
"typeVersion": 1
},
{
"id": "b351d8be-cb6f-4a28-8c9b-be08ed823ac2",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3040,
-11312
],
"parameters": {
"color": 7,
"width": 352,
"height": 256,
"content": "## Query Preprocessing\nCleans and structures the customer message (cleanedQuery, keywords, chatInput) to improve retrieval and response quality."
},
"typeVersion": 1
},
{
"id": "d8a97c4b-0172-4bb7-9458-1229665ca9f8",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2624,
-11312
],
"parameters": {
"color": 7,
"width": 512,
"height": 480,
"content": "## RAG Answer Generation\nGenerates a grounded support answer using the knowledge base + conversation context (RAG). Uses the configured OpenAI chat model."
},
"typeVersion": 1
},
{
"id": "9b02c505-3070-4b1e-89c7-4ac9f6fa02ca",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2032,
-11312
],
"parameters": {
"color": 7,
"width": 704,
"height": 480,
"content": "## Quality & Risk Gating\nClassifies sentiment and computes a confidence score for the AI response. Gates the flow based on confidence threshold and sentiment polarity."
},
"typeVersion": 1
},
{
"id": "d7d5f130-2379-4d75-94f2-bfca68fd4527",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1248,
-11568
],
"parameters": {
"color": 7,
"width": 912,
"height": 320,
"content": "## Scope & Human Takeover Guardrails\nChecks if the request is within scope and detects explicit human takeover intent. Routes either to response delivery or escalation."
},
"typeVersion": 1
},
{
"id": "5d3e881c-7161-4f82-8681-ed15c9cfc7fd",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1248,
-10912
],
"parameters": {
"color": 7,
"width": 1264,
"height": 464,
"content": "## Escalation to Human Support\nExplains why escalation is required, prepares a ticket payload, checks business hours, creates/updates a Zendesk ticket, and alerts the support team in Slack."
},
"typeVersion": 1
},
{
"id": "ee22fb1e-655f-42bd-a817-dbe393d905f8",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
-304,
-11968
],
"parameters": {
"color": 7,
"width": 1392,
"height": 1024,
"content": "## Channel Response Delivery\nRoutes the AI response back to the original channel and applies channel-specific formatting. Email replies are sent; other channels end after formatting."
},
"typeVersion": 1
},
{
"id": "75613c6d-f04f-405b-8a5b-d0108c90b8c0",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2624,
-10784
],
"parameters": {
"color": 4,
"width": 1296,
"height": 800,
"content": "## Weekly Maintenance & Vector Updates\nRuns weekly: fetches metrics from Sheets, prepares content, splits + embeds documents, inserts into Supabase vector store, and posts a Slack weekly summary."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "48e5f374-154d-436e-8335-f6732d97bfa2",
"connections": {
"Chat Memory": {
"ai_memory": [
[
{
"node": "RAG Support Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"Text Splitter": {
"ai_textSplitter": [
[
{
"node": "Document Loader",
"type": "ai_textSplitter",
"index": 0
}
]
]
},
"Channel Router": {
"main": [
[
{
"node": "Format Email Response",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Webhook Response",
"type": "main",
"index": 0
}
],
[
{
"node": "Format WhatsApp Response",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Discord Response",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Slack Response",
"type": "main",
"index": 0
}
]
]
},
"Check Sentiment": {
"main": [
[
{
"node": "Check Message Scope",
"type": "main",
"index": 0
}
],
[
{
"node": "Escalation Needed?",
"type": "main",
"index": 0
}
]
]
},
"Document Loader": {
"ai_document": [
[
{
"node": "Supabase Vector Insert",
"type": "ai_document",
"index": 0
}
]
]
},
"Preprocess Query": {
"main": [
[
{
"node": "RAG Support Agent",
"type": "main",
"index": 0
}
]
]
},
"Send Email Reply": {
"main": [
[
{
"node": "Auto-Categorization",
"type": "main",
"index": 0
}
]
]
},
"WhatsApp Trigger": {
"main": [
[
{
"node": "Workflow Configuration",
"type": "main",
"index": 0
}
]
]
},
"Normalize Payload": {
"main": [
[
{
"node": "Conversation History Manager",
"type": "main",
"index": 0
}
]
]
},
"RAG Support Agent": {
"main": [
[
{
"node": "Sentiment Analysis",
"type": "main",
"index": 0
}
]
]
},
"Confidence Scoring": {
"main": [
[
{
"node": "Check Confidence Threshold",
"type": "main",
"index": 0
}
]
]
},
"Escalation Needed?": {
"main": [
[
{
"node": "Escalation Reasoning",
"type": "main",
"index": 0
}
],
[]
]
},
"Sentiment Analysis": {
"main": [
[
{
"node": "Confidence Scoring",
"type": "main",
"index": 0
}
]
]
},
"Auto-Categorization": {
"main": [
[
{
"node": "Log Conversation to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Check Message Scope": {
"main": [
[
{
"node": "Human Takeover Detection",
"type": "main",
"index": 0
}
],
[
{
"node": "Escalation Needed?",
"type": "main",
"index": 0
}
]
]
},
"Business Hours Check": {
"main": [
[
{
"node": "Create Zendesk Ticket",
"type": "main",
"index": 0
}
],
[
{
"node": "Update Zendesk Ticket",
"type": "main",
"index": 0
}
]
]
},
"Email Trigger (IMAP)": {
"main": [
[
{
"node": "Workflow Configuration",
"type": "main",
"index": 0
}
]
]
},
"Escalation Reasoning": {
"main": [
[
{
"node": "Prepare Ticket Object",
"type": "main",
"index": 0
}
]
]
},
"Fetch Weekly Metrics": {
"main": [
[
{
"node": "Supabase Vector Insert",
"type": "main",
"index": 0
},
{
"node": "Prepare Metrics for Vector Insert",
"type": "main",
"index": 0
}
]
]
},
"Create Zendesk Ticket": {
"main": [
[
{
"node": "Escalation Alert to Support Team",
"type": "main",
"index": 0
}
]
]
},
"Format Email Response": {
"main": [
[
{
"node": "Send Email Reply",
"type": "main",
"index": 0
}
]
]
},
"Format Slack Response": {
"main": [
[
{
"node": "End Workflow",
"type": "main",
"index": 0
}
]
]
},
"Prepare Ticket Object": {
"main": [
[
{
"node": "Business Hours Check",
"type": "main",
"index": 0
}
]
]
},
"Update Zendesk Ticket": {
"main": [
[
{
"node": "Escalation Alert to Support Team",
"type": "main",
"index": 0
}
]
]
},
"Supabase Vector Insert": {
"main": [
[
{
"node": "Send Weekly Summary",
"type": "main",
"index": 0
}
]
]
},
"Workflow Configuration": {
"main": [
[
{
"node": "Normalize Payload",
"type": "main",
"index": 0
}
]
]
},
"Discord Webhook Trigger": {
"main": [
[
{
"node": "Workflow Configuration",
"type": "main",
"index": 0
}
]
]
},
"Format Discord Response": {
"main": [
[
{
"node": "End Workflow",
"type": "main",
"index": 0
}
]
]
},
"Format Webhook Response": {
"main": [
[
{
"node": "End Workflow",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model (RAG)": {
"ai_languageModel": [
[
{
"node": "RAG Support Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Format WhatsApp Response": {
"main": [
[
{
"node": "End Workflow",
"type": "main",
"index": 0
}
]
]
},
"Human Takeover Detection": {
"main": [
[
{
"node": "Channel Router",
"type": "main",
"index": 0
}
],
[
{
"node": "Escalation Needed?",
"type": "main",
"index": 0
}
]
]
},
"Merge History with Query": {
"main": [
[
{
"node": "Preprocess Query",
"type": "main",
"index": 0
}
]
]
},
"Slack Trigger (Mentions)": {
"main": [
[
{
"node": "Workflow Configuration",
"type": "main",
"index": 0
}
]
]
},
"Check Confidence Threshold": {
"main": [
[
{
"node": "Check Sentiment",
"type": "main",
"index": 0
}
],
[
{
"node": "Escalation Needed?",
"type": "main",
"index": 0
}
]
]
},
"Fetch Conversation History": {
"main": [
[
{
"node": "Merge History with Query",
"type": "main",
"index": 0
}
]
]
},
"Log Conversation to Sheets": {
"main": [
[
{
"node": "Log Conversation to Zendesk",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Embeddings (Insert)": {
"ai_embedding": [
[
{
"node": "Supabase Vector Insert",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Webhook Trigger (Live Chat)": {
"main": [
[
{
"node": "Workflow Configuration",
"type": "main",
"index": 0
}
]
]
},
"Weekly Maintenance Schedule": {
"main": [
[
{
"node": "Fetch Weekly Metrics",
"type": "main",
"index": 0
}
]
]
},
"Conversation History Manager": {
"main": [
[
{
"node": "New vs Existing Conversation",
"type": "main",
"index": 0
}
]
]
},
"New vs Existing Conversation": {
"main": [
[
{
"node": "Preprocess Query",
"type": "main",
"index": 0
}
],
[
{
"node": "Fetch Conversation History",
"type": "main",
"index": 0
},
{
"node": "Merge History with Query",
"type": "main",
"index": 1
}
]
]
},
"OpenAI Chat Model (Sentiment)": {
"ai_languageModel": [
[
{
"node": "Sentiment Analysis",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"OpenAI Chat Model (Escalation)": {
"ai_languageModel": [
[
{
"node": "Escalation Reasoning",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Escalation Alert to Support Team": {
"main": [
[]
]
},
"Prepare Metrics for Vector Insert": {
"main": [
[
{
"node": "Supabase Vector Insert",
"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.
googleApiimapopenAiApislackApismtpsupabaseApiwhatsAppTriggerApizendeskApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automates customer support across multiple channels (Email, Live Chat, WhatsApp, Slack, Discord) using AI-powered responses enhanced with Retrieval Augmented Generation (RAG) and your product documentation. It intelligently handles incoming queries, provides…
Source: https://n8n.io/workflows/11807/ — 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.
Hi! I’m Amanda, a creator of intelligent automations using n8n and Make. I’ve been building AI-powered workflows for over 2 years, always focused on usability and innovation. This one here is very spe
YouTube Agent. Uses supabase, agent, lmChatAnthropic, outputParserStructured. Webhook trigger; 56 nodes.
AI Multi-Document Analyzer with Smart Recommendations & Reporting
This workflow implements a complete Voice AI Chatbot system for Wordress that integrates speech recognition, guardrails for safety, retrieval-augmented generation (RAG), Qdrant vector search, and audi
This comprehensive n8n workflow template creates an intelligent AI chatbot that automatically transforms your Google Drive documents into a searchable knowledge base. The chatbot uses OpenAI's GPT mod