This workflow follows the Gmail Trigger → Google Sheets 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 →
{
"updatedAt": "2026-05-14T20:01:24.893Z",
"createdAt": "2025-09-11T19:49:07.661Z",
"id": "lLmdtJWpmMd86l0W",
"name": "Self-Learning FAQs RAG",
"active": true,
"isArchived": false,
"nodes": [
{
"parameters": {},
"id": "bb7938da-83ec-43ec-8709-e800e921518e",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
0,
0
]
},
{
"parameters": {
"content": "## Embed each FAQ question and store it in Supabase.\n\nFlow:\n1. Input: Each item is a {question, answer} object from the \"Split Q&A Items\" node.\n2. OpenAI Embeddings node:\n - Model: text-embedding-3-small (1536-dim)\n - Input: {{$json[\"question\"]}}\n - Output: vector array in {{$json[\"data\"][0][\"embedding\"]}}\n3. Supabase Insert node:\n - Inserts one row per FAQ into \"faqs\" table\n - question \u2192 {{$json[\"question\"]}}\n - answer \u2192 {{$json[\"answer\"]}}\n - embedding \u2192 {{$json[\"data\"][0][\"embedding\"]}}\n\nResult: \nEach FAQ pair is stored in Supabase with a vector embedding for similarity search.\n",
"height": 704,
"width": 1008
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-80,
-464
],
"id": "051644b7-9cd9-4ef9-8d7f-488268efe70c",
"name": "Sticky Note"
},
{
"parameters": {
"operation": "get",
"documentURL": "1j5jKcgPaHPnO5j7fpiNBVjo6_6Y_yjwkEwV7oOmm6Us"
},
"type": "n8n-nodes-base.googleDocs",
"typeVersion": 2,
"position": [
208,
0
],
"id": "2d002f5d-bd05-4f82-8b59-627655ce04da",
"name": "Load SOP",
"credentials": {
"googleDocsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Grab the raw SOP text from Google Docs\nconst raw = $json[\"content\"] || \"\";\n\n// Split into blocks by \"----\"\nconst blocks = raw.split(/----+/).map(b => b.trim()).filter(b => b.length > 0);\n\n// Return each block as an item\nreturn blocks.map(block => ({\n json: { block }\n}));\n\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
416,
0
],
"id": "ce7ce44f-6388-465a-b6b9-94aef5ae73d0",
"name": "Extract FAQs"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "GPT-4.1-MINI"
},
"messages": {
"values": [
{
"content": "You are a strict JSON parser.\n\nYou receive one block of text at a time from a long German SOP document.\nSometimes the block contains a FAQ entry (a question + an answer).\nOther times it contains instructions, templates, disclaimers, or irrelevant text.\n\nYour job:\n- If the block contains a FAQ entry, extract the question and answer in German AND also translate them into English.\n- If the block is not a FAQ (no clear question/answer), return {\"skip\": true}.\n\nRules:\n- Always return valid JSON.\n- Preserve line breaks in answers.\n- Do not invent or summarize. Copy text faithfully.\n",
"role": "system"
},
{
"content": "={{ $json.block }}\n\nReturn JSON in this exact schema:\n{\n \"skip\": boolean,\n \"question_de\": string,\n \"answer_de\": string,\n \"question_en\": string,\n \"answer_en\": string\n}"
}
]
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [
624,
0
],
"id": "af886f44-42fe-4d36-a003-39ef079b95a2",
"name": "Message a model",
"retryOnFail": true,
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "6f83f26b-842f-4812-95e6-d2596420e463",
"name": "message.content",
"value": "={{ $json.message.content }}",
"type": "object"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
0,
272
],
"id": "83fc4cdd-17a2-4d01-8678-3b6afc330f3f",
"name": "Edit Fields"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "f2e0e92e-f223-412d-a5fa-12d853df9a5d",
"leftValue": "={{ $json.message.content.skip }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "notEquals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.filter",
"typeVersion": 2.2,
"position": [
208,
272
],
"id": "70e556b9-2a91-4694-90ac-e255d6e0defd",
"name": "Filter"
},
{
"parameters": {
"useCustomSchema": true,
"tableId": "faqs",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "question",
"fieldValue": "={{ $('Filter').item.json.message.content.question_de }}"
},
{
"fieldId": "answer",
"fieldValue": "={{ $('Filter').item.json.message.content.answer_de }}"
},
{
"fieldId": "embedding",
"fieldValue": "={{ $json.data[0].embedding }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
752,
272
],
"id": "57050609-e249-4b99-87d7-044ba431ad14",
"name": "Create a row",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/embeddings",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "text-embedding-3-small"
},
{
"name": "input",
"value": "={{ $json.message.content.question_de }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
416,
272
],
"id": "b8c85928-cbaa-4c70-ad3d-82504c2e3098",
"name": "Generate Embeddings",
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"simple": false,
"filters": {
"labelIds": "={{ [\"INBOX\"] }}",
"q": "{ from:anna.streeb@deutsches-edelsteinhaus.com from:adamhaley@gmail.com}"
},
"options": {}
},
"type": "n8n-nodes-base.gmailTrigger",
"typeVersion": 1.3,
"position": [
1280,
-16
],
"id": "19855303-d5cc-4bd9-9b6c-ec5ca53e8a14",
"name": "Gmail Trigger",
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Get email text\nconst text = $json.text || $input.first().json.test_email || \"\";\n\n// --------------------------------------\n// Extract first / last name\n// Supports both German and English labels\n// --------------------------------------\nconst firstMatch = text.match(/(?:Vorname|First name):\\s*([^<\\n\\r]*)/i);\nconst lastMatch = text.match(/(?:Nachname|Last name):\\s*([^<\\n\\r]*)/i);\n\nconst firstname = firstMatch ? firstMatch[1].trim() : \"\";\n\nlet lastname = \"\";\nif (lastMatch && lastMatch[1]) {\n const rawLast = lastMatch[1].trim();\n\n // Avoid accidentally grabbing the next field if lastname is blank\n if (!/^(E-Mail|Email|Phone|Telefon|Callback|Note|Hinweis)/i.test(rawLast)) {\n lastname = rawLast;\n }\n}\n\n// Build fullname\nlet fullname = firstname;\nif (lastname) {\n fullname = `${firstname} ${lastname}`;\n}\n\n// --------------------------------------\n// Extract attendee email address\n// Supports:\n// Email:\n// E-Mail:\n// mailto:\n// plain email fallback\n// --------------------------------------\nlet replyTo = null;\n\n// 1. Prefer labeled Email / E-Mail field\nlet emailMatch = text.match(\n /(?:E-Mail|Email):\\s*(?:<a[^>]*href=[\"']mailto:)?([^\\s<>\"']+@[^\\s<>\"']+)/i\n);\n\n// 2. Fallback: any mailto link\nif (!emailMatch) {\n emailMatch = text.match(/mailto:([^\\s<>\"']+@[^\\s<>\"']+)/i);\n}\n\n// 3. Last fallback: any email address in the body\nif (!emailMatch) {\n emailMatch = text.match(/([A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,})/i);\n}\n\nif (emailMatch) {\n replyTo = emailMatch[1]\n .trim()\n .replace(/[.,;]+$/, \"\")\n .toLowerCase();\n}\n\n// --------------------------------------\n// Extract \"Chat:\" section only\n// --------------------------------------\nconst chatSection = text.split(/Chat:/i)[1] || \"\";\n\n// --------------------------------------\n// Parse questions\n// --------------------------------------\nconst questions = [];\n\n// Matches lines like:\n// 1: Tatjana (10/09/2025 10:58): Question text...\nconst regex = /\\d+:\\s.*?\\):\\s([\\s\\S]*?)(?=\\n\\s*\\d+:\\s|$)/g;\n\nlet match;\n\nwhile ((match = regex.exec(chatSection)) !== null) {\n let q = match[1].trim();\n\n // Convert HTML line breaks to spaces\n q = q.replace(/<br\\s*\\/?>/gi, \" \");\n\n // Strip basic HTML tags\n q = q.replace(/<[^>]+>/g, \" \");\n\n // Normalize whitespace\n q = q.replace(/\\s+/g, \" \").trim();\n\n // Stop parsing at common footer / signoff phrases\n if (/Vielen Dank|Thank you in advance|Best regards|Kind regards|Webinaris|Hinweis:|Note:/i.test(q)) {\n break;\n }\n\n // Filter out boilerplate lines\n if (\n q &&\n !/Vielen Dank|Thank you in advance|Best regards|Kind regards|Webinaris|Hinweis:|Note:/i.test(q)\n ) {\n questions.push({ question: q });\n }\n}\n\n// --------------------------------------\n// Return data\n// IMPORTANT: keeps top-level \"questions\"\n// --------------------------------------\nreturn {\n threadId: $input.first().json.threadId || \"none\",\n replyTo: replyTo || \"not_found@example.com\",\n replyToName: {\n fullname,\n firstname,\n lastname\n },\n questions: questions.map(q => ({ json: q }))\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1648,
-16
],
"id": "26b4089f-6513-4aef-8b1c-f08485cb67c5",
"name": "Parse Questions"
},
{
"parameters": {
"method": "POST",
"url": "https://supabase.megyk.com/rest/v1/rpc//match_faqs_json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "query_embedding",
"value": "={{ $json.data[0].embedding }}"
},
{
"name": "match_count",
"value": "5"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": true,
"responseFormat": "json"
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2288,
-16
],
"id": "975f770e-1c1f-4b4a-a237-9228b0e99953",
"name": "Supabase RPC Retrieval",
"alwaysOutputData": false,
"executeOnce": false,
"credentials": {
"httpCustomAuth": {
"name": "<your credential>"
},
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "## Retrieval ( Auto Q & A Flow )\n### Retrieval (Auto Q & A Flow)\n\nFlow: \n1. **Trigger:** Gmail node receives new survey response with user questions. \n2. **Parse Questions:** Extract raw text of each user question. \n3. **OpenAI Embeddings:** \n - Model: `text-embedding-3-small` (1536-dim) \n - Input: `{{$json[\"question\"]}}` (user\u2019s question in German) \n - Output: `{{$json[\"data\"][0][\"embedding\"]}}` \n4. **Supabase RPC Retrieval:** \n - Calls `match_faqs_json(query_embedding, match_count=5)` \n - Returns top FAQ matches with similarity scores. \n5. **AI Reply (next step):** \n - Pass best match into OpenAI Chat node \n - Draft personalized auto-reply email using FAQ answer as context \n - Format into Gmail/Bitrix email template \n\n",
"height": 912,
"width": 1408,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1088,
-464
],
"id": "de68b760-5b4d-470e-b296-db392d007c8a",
"name": "Sticky Note1"
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/embeddings",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "text-embedding-3-small"
},
{
"name": "input",
"value": "={{ $json.json.question }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2064,
-16
],
"id": "ebf84add-dfbb-458c-9415-8a043c40d877",
"name": "Question Embedding",
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1RlfPHGwn5uZ1ucIdLnk_6ZFPJPfhTpa8uQwsYuJCYug",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": 651789693,
"mode": "list",
"cachedResultName": "Copy of Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1RlfPHGwn5uZ1ucIdLnk_6ZFPJPfhTpa8uQwsYuJCYug/edit#gid=651789693"
}
},
"type": "n8n-nodes-base.evaluationTrigger",
"typeVersion": 4.6,
"position": [
624,
432
],
"id": "71cc0514-e666-4a83-893e-8a15946874cc",
"name": "When fetching a dataset row",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1RlfPHGwn5uZ1ucIdLnk_6ZFPJPfhTpa8uQwsYuJCYug",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": 651789693,
"mode": "list",
"cachedResultName": "Copy of Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1RlfPHGwn5uZ1ucIdLnk_6ZFPJPfhTpa8uQwsYuJCYug/edit#gid=651789693"
},
"outputs": {
"values": [
{
"outputName": "answer_correctness",
"outputValue": "={{ $json.Correctness }}"
},
{
"outputName": "generated_answer",
"outputValue": "={{ $('Generate Email Reply Segment').item.json.message.content }}"
},
{
"outputName": "result",
"outputValue": "={{ ($json.Correctness >=2)? 'Pass' : 'Fail' }}"
},
{
"outputName": "actual_faq_match",
"outputValue": "={{ $('Supabase RPC Retrieval').item.json.body[0].id }}"
}
]
}
},
"type": "n8n-nodes-base.evaluation",
"typeVersion": 4.7,
"position": [
1536,
512
],
"id": "337fb1b5-c32d-4a64-8e74-0ee42d8c8132",
"name": "Evaluation",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "setMetrics",
"expectedAnswer": "={{ $('Supabase RPC Retrieval').item.json.body[0].answer }}",
"actualAnswer": "={{ $json.message.content }}",
"options": {}
},
"type": "n8n-nodes-base.evaluation",
"typeVersion": 4.7,
"position": [
1184,
512
],
"id": "b3dc6f0a-8e28-4525-87a0-ad0faaf08f9e",
"name": "Evaluation1"
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
1248,
736
],
"id": "11ed9aac-dc7e-4f06-a480-f2a5e22b18bb",
"name": "OpenAI Chat Model",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "openrouter/free",
"mode": "list",
"cachedResultName": "OPENROUTER/FREE"
},
"messages": {
"values": [
{
"content": "=FAQ Matches:\n{{ JSON.stringify($json.body) }}\n\nHuman-approved response for this matched FAQ, if available:\n{{$json.body[0].approved_response || 'No human-approved response available.' }}\n\nUser Question:\n{{ $('Split Out').item.json.json.question }}\n\nInstructions:\nAnswer the user question using the retrieved FAQ answers.\n\nIf a human-approved response is available, use it as the preferred answer pattern and adapt it naturally to the current question.\n\n{{\n $itemIndex === $items().length - 1\n ? \"- Answer in a few sentences, and answer in a way that elicits curiosity from the user to take the next step and schedule an appointment. Build curiosity and anticipation within the answer so the participant is interested in hearing more and discussing it personally.\"\n : \"\"\n}}"
},
{
"content": "You are a helpful support assistant for the Deutsches Edelsteinhaus FAQ system.\n\nAlways answer in German unless the customer explicitly asks for English.\n\nUse the retrieved FAQ answers as your source of truth. You may rephrase, clarify, or combine them for a natural reply, but you must not invent facts beyond what is provided.\n\nIf a human-approved response is provided, treat it as preferred guidance for this matched FAQ. Use it to align tone, structure, wording, and business positioning. Stay close to it when appropriate, but adapt naturally to the current customer question.\n\nIf the human-approved response conflicts with the retrieved FAQ answers, prefer the retrieved FAQ answers.\n\nIf the FAQ answers are incomplete, politely acknowledge the limits and keep the answer conservative.\n\nKeep a professional but warm tone suitable for email replies to potential investors.\n\nFormat your answer as a segment of an email body with no greeting and no closing signature. The answer segments will be joined together later.",
"role": "system"
}
]
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [
1280,
224
],
"id": "4fc5f1d9-4770-41aa-9f20-bbb186c03db3",
"name": "Generate Email Reply Segment",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"fieldToSplitOut": "questions",
"options": {}
},
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [
1856,
-16
],
"id": "70a7b9ea-11da-4987-bac7-9230262e8aa6",
"name": "Split Out"
},
{
"parameters": {
"operation": "appendOrUpdate",
"documentId": {
"__rl": true,
"value": "10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50",
"mode": "list",
"cachedResultName": "FAQ Responder Approvals",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Question": "={{ $('Split Out').item.json.json.question }}",
"date": "={{ $now.format('yyyy-MM-dd') }}",
"Agent Response": "={{ $json.message.content }}",
"Reply To": "={{ $('Parse Questions').item.json.replyTo }}",
"match_score": "={{ $('Supabase RPC Retrieval').item.json.body.first().similarity }}",
"match_uuid": "={{ $('Supabase RPC Retrieval').item.json.body.first().id}}",
"thread_id": "={{ $('Gmail Trigger').item.json.threadId }}",
"Reply To Name": "={{ $('Parse Questions').item.json.replyToName.fullname }}"
},
"matchingColumns": [
"Question"
],
"schema": [
{
"id": "date",
"displayName": "date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "thread_id",
"displayName": "thread_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "match_uuid",
"displayName": "match_uuid",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "match_score",
"displayName": "match_score",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Reply To",
"displayName": "Reply To",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Reply To Name",
"displayName": "Reply To Name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Question",
"displayName": "Question",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Agent Response",
"displayName": "Agent Response",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Approved",
"displayName": "Approved",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "Sent",
"displayName": "Sent",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
1648,
224
],
"id": "099f0d19-927b-4d0d-90d6-4a1ea0a18cb0",
"name": "Append or update row in sheet",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Manual Trigger": {
"main": [
[
{
"node": "Load SOP",
"type": "main",
"index": 0
}
]
]
},
"Load SOP": {
"main": [
[
{
"node": "Extract FAQs",
"type": "main",
"index": 0
}
]
]
},
"Extract FAQs": {
"main": [
[
{
"node": "Message a model",
"type": "main",
"index": 0
}
]
]
},
"Message a model": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Filter",
"type": "main",
"index": 0
}
]
]
},
"Filter": {
"main": [
[
{
"node": "Generate Embeddings",
"type": "main",
"index": 0
}
]
]
},
"Generate Embeddings": {
"main": [
[
{
"node": "Create a row",
"type": "main",
"index": 0
}
]
]
},
"Gmail Trigger": {
"main": [
[
{
"node": "Parse Questions",
"type": "main",
"index": 0
}
]
]
},
"Parse Questions": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
},
"Supabase RPC Retrieval": {
"main": [
[
{
"node": "Generate Email Reply Segment",
"type": "main",
"index": 0
}
]
]
},
"Question Embedding": {
"main": [
[
{
"node": "Supabase RPC Retrieval",
"type": "main",
"index": 0
}
]
]
},
"When fetching a dataset row": {
"main": [
[]
]
},
"Evaluation1": {
"main": [
[
{
"node": "Evaluation",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Evaluation1",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Generate Email Reply Segment": {
"main": [
[
{
"node": "Append or update row in sheet",
"type": "main",
"index": 0
}
]
]
},
"Split Out": {
"main": [
[
{
"node": "Question Embedding",
"type": "main",
"index": 0
}
]
]
},
"Append or update row in sheet": {
"main": [
[]
]
}
},
"settings": {
"executionOrder": "v1",
"saveExecutionProgress": true,
"callerPolicy": "workflowsFromSameOwner",
"errorWorkflow": "WbISMAIlclQfzorG",
"binaryMode": "separate"
},
"staticData": {
"node:Gmail Trigger": {
"Gmail Trigger": {
"lastTimeChecked": 1778576297,
"possibleDuplicates": [
"19e1b6898050f032"
]
}
},
"node:Email Trigger (IMAP)": {
"lastMessageUid": 6
},
"node:Google Sheets Trigger": {
"documentId": "10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50",
"sheetId": 0,
"lastRevision": 138,
"lastRevisionLink": "https://docs.google.com/spreadsheets/export?id=10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50&revision=138&exportFormat=xlsx"
},
"node:Google Sheets Trigger1": {
"documentId": "10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50",
"sheetId": 0,
"lastRevision": 138,
"lastRevisionLink": "https://docs.google.com/spreadsheets/export?id=10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50&revision=138&exportFormat=xlsx"
}
},
"meta": {
"templateCredsSetupCompleted": true
},
"versionId": "73bbe74f-9c26-4c6b-9294-101fc95464be",
"activeVersionId": "73bbe74f-9c26-4c6b-9294-101fc95464be",
"triggerCount": 1,
"shared": [
{
"updatedAt": "2025-09-11T19:49:07.676Z",
"createdAt": "2025-09-11T19:49:07.676Z",
"role": "workflow:owner",
"workflowId": "lLmdtJWpmMd86l0W",
"projectId": "B7QJE85HA2Vij1it"
}
],
"activeVersion": {
"updatedAt": "2026-05-14T20:02:35.000Z",
"createdAt": "2026-05-14T20:01:24.896Z",
"versionId": "73bbe74f-9c26-4c6b-9294-101fc95464be",
"workflowId": "lLmdtJWpmMd86l0W",
"nodes": [
{
"parameters": {},
"id": "bb7938da-83ec-43ec-8709-e800e921518e",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
0,
0
]
},
{
"parameters": {
"content": "## Embed each FAQ question and store it in Supabase.\n\nFlow:\n1. Input: Each item is a {question, answer} object from the \"Split Q&A Items\" node.\n2. OpenAI Embeddings node:\n - Model: text-embedding-3-small (1536-dim)\n - Input: {{$json[\"question\"]}}\n - Output: vector array in {{$json[\"data\"][0][\"embedding\"]}}\n3. Supabase Insert node:\n - Inserts one row per FAQ into \"faqs\" table\n - question \u2192 {{$json[\"question\"]}}\n - answer \u2192 {{$json[\"answer\"]}}\n - embedding \u2192 {{$json[\"data\"][0][\"embedding\"]}}\n\nResult: \nEach FAQ pair is stored in Supabase with a vector embedding for similarity search.\n",
"height": 704,
"width": 1008
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-80,
-464
],
"id": "051644b7-9cd9-4ef9-8d7f-488268efe70c",
"name": "Sticky Note"
},
{
"parameters": {
"operation": "get",
"documentURL": "1j5jKcgPaHPnO5j7fpiNBVjo6_6Y_yjwkEwV7oOmm6Us"
},
"type": "n8n-nodes-base.googleDocs",
"typeVersion": 2,
"position": [
208,
0
],
"id": "2d002f5d-bd05-4f82-8b59-627655ce04da",
"name": "Load SOP",
"credentials": {
"googleDocsOAuth2Api": {
"id": "qVRiWRvOhvPAHWEY",
"name": "Google Docs account"
}
}
},
{
"parameters": {
"jsCode": "// Grab the raw SOP text from Google Docs\nconst raw = $json[\"content\"] || \"\";\n\n// Split into blocks by \"----\"\nconst blocks = raw.split(/----+/).map(b => b.trim()).filter(b => b.length > 0);\n\n// Return each block as an item\nreturn blocks.map(block => ({\n json: { block }\n}));\n\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
416,
0
],
"id": "ce7ce44f-6388-465a-b6b9-94aef5ae73d0",
"name": "Extract FAQs"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "GPT-4.1-MINI"
},
"messages": {
"values": [
{
"content": "You are a strict JSON parser.\n\nYou receive one block of text at a time from a long German SOP document.\nSometimes the block contains a FAQ entry (a question + an answer).\nOther times it contains instructions, templates, disclaimers, or irrelevant text.\n\nYour job:\n- If the block contains a FAQ entry, extract the question and answer in German AND also translate them into English.\n- If the block is not a FAQ (no clear question/answer), return {\"skip\": true}.\n\nRules:\n- Always return valid JSON.\n- Preserve line breaks in answers.\n- Do not invent or summarize. Copy text faithfully.\n",
"role": "system"
},
{
"content": "={{ $json.block }}\n\nReturn JSON in this exact schema:\n{\n \"skip\": boolean,\n \"question_de\": string,\n \"answer_de\": string,\n \"question_en\": string,\n \"answer_en\": string\n}"
}
]
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [
624,
0
],
"id": "af886f44-42fe-4d36-a003-39ef079b95a2",
"name": "Message a model",
"retryOnFail": true,
"credentials": {
"openAiApi": {
"id": "BRRf66J5aSwt4UDP",
"name": "OpenAi account"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "6f83f26b-842f-4812-95e6-d2596420e463",
"name": "message.content",
"value": "={{ $json.message.content }}",
"type": "object"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
0,
272
],
"id": "83fc4cdd-17a2-4d01-8678-3b6afc330f3f",
"name": "Edit Fields"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "f2e0e92e-f223-412d-a5fa-12d853df9a5d",
"leftValue": "={{ $json.message.content.skip }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "notEquals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.filter",
"typeVersion": 2.2,
"position": [
208,
272
],
"id": "70e556b9-2a91-4694-90ac-e255d6e0defd",
"name": "Filter"
},
{
"parameters": {
"useCustomSchema": true,
"tableId": "faqs",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "question",
"fieldValue": "={{ $('Filter').item.json.message.content.question_de }}"
},
{
"fieldId": "answer",
"fieldValue": "={{ $('Filter').item.json.message.content.answer_de }}"
},
{
"fieldId": "embedding",
"fieldValue": "={{ $json.data[0].embedding }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
752,
272
],
"id": "57050609-e249-4b99-87d7-044ba431ad14",
"name": "Create a row",
"credentials": {
"supabaseApi": {
"id": "fdzgJDGuPA2JozKn",
"name": "Supabase account"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/embeddings",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "text-embedding-3-small"
},
{
"name": "input",
"value": "={{ $json.message.content.question_de }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
416,
272
],
"id": "b8c85928-cbaa-4c70-ad3d-82504c2e3098",
"name": "Generate Embeddings",
"credentials": {
"httpBearerAuth": {
"id": "PuW4D772YJeXzDlO",
"name": "Bearer Auth account"
}
}
},
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"simple": false,
"filters": {
"labelIds": "={{ [\"INBOX\"] }}",
"q": "{ from:anna.streeb@deutsches-edelsteinhaus.com from:adamhaley@gmail.com}"
},
"options": {}
},
"type": "n8n-nodes-base.gmailTrigger",
"typeVersion": 1.3,
"position": [
1280,
-16
],
"id": "19855303-d5cc-4bd9-9b6c-ec5ca53e8a14",
"name": "Gmail Trigger",
"credentials": {
"gmailOAuth2": {
"id": "ElDhEFB9UngCuzGT",
"name": "Deutsches EdelsteinHaus Gmail"
}
}
},
{
"parameters": {
"jsCode": "// Get email text\nconst text = $json.text || $input.first().json.test_email || \"\";\n\n// --------------------------------------\n// Extract first / last name\n// Supports both German and English labels\n// --------------------------------------\nconst firstMatch = text.match(/(?:Vorname|First name):\\s*([^<\\n\\r]*)/i);\nconst lastMatch = text.match(/(?:Nachname|Last name):\\s*([^<\\n\\r]*)/i);\n\nconst firstname = firstMatch ? firstMatch[1].trim() : \"\";\n\nlet lastname = \"\";\nif (lastMatch && lastMatch[1]) {\n const rawLast = lastMatch[1].trim();\n\n // Avoid accidentally grabbing the next field if lastname is blank\n if (!/^(E-Mail|Email|Phone|Telefon|Callback|Note|Hinweis)/i.test(rawLast)) {\n lastname = rawLast;\n }\n}\n\n// Build fullname\nlet fullname = firstname;\nif (lastname) {\n fullname = `${firstname} ${lastname}`;\n}\n\n// --------------------------------------\n// Extract attendee email address\n// Supports:\n// Email:\n// E-Mail:\n// mailto:\n// plain email fallback\n// --------------------------------------\nlet replyTo = null;\n\n// 1. Prefer labeled Email / E-Mail field\nlet emailMatch = text.match(\n /(?:E-Mail|Email):\\s*(?:<a[^>]*href=[\"']mailto:)?([^\\s<>\"']+@[^\\s<>\"']+)/i\n);\n\n// 2. Fallback: any mailto link\nif (!emailMatch) {\n emailMatch = text.match(/mailto:([^\\s<>\"']+@[^\\s<>\"']+)/i);\n}\n\n// 3. Last fallback: any email address in the body\nif (!emailMatch) {\n emailMatch = text.match(/([A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,})/i);\n}\n\nif (emailMatch) {\n replyTo = emailMatch[1]\n .trim()\n .replace(/[.,;]+$/, \"\")\n .toLowerCase();\n}\n\n// --------------------------------------\n// Extract \"Chat:\" section only\n// --------------------------------------\nconst chatSection = text.split(/Chat:/i)[1] || \"\";\n\n// --------------------------------------\n// Parse questions\n// --------------------------------------\nconst questions = [];\n\n// Matches lines like:\n// 1: Tatjana (10/09/2025 10:58): Question text...\nconst regex = /\\d+:\\s.*?\\):\\s([\\s\\S]*?)(?=\\n\\s*\\d+:\\s|$)/g;\n\nlet match;\n\nwhile ((match = regex.exec(chatSection)) !== null) {\n let q = match[1].trim();\n\n // Convert HTML line breaks to spaces\n q = q.replace(/<br\\s*\\/?>/gi, \" \");\n\n // Strip basic HTML tags\n q = q.replace(/<[^>]+>/g, \" \");\n\n // Normalize whitespace\n q = q.replace(/\\s+/g, \" \").trim();\n\n // Stop parsing at common footer / signoff phrases\n if (/Vielen Dank|Thank you in advance|Best regards|Kind regards|Webinaris|Hinweis:|Note:/i.test(q)) {\n break;\n }\n\n // Filter out boilerplate lines\n if (\n q &&\n !/Vielen Dank|Thank you in advance|Best regards|Kind regards|Webinaris|Hinweis:|Note:/i.test(q)\n ) {\n questions.push({ question: q });\n }\n}\n\n// --------------------------------------\n// Return data\n// IMPORTANT: keeps top-level \"questions\"\n// --------------------------------------\nreturn {\n threadId: $input.first().json.threadId || \"none\",\n replyTo: replyTo || \"not_found@example.com\",\n replyToName: {\n fullname,\n firstname,\n lastname\n },\n questions: questions.map(q => ({ json: q }))\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1648,
-16
],
"id": "26b4089f-6513-4aef-8b1c-f08485cb67c5",
"name": "Parse Questions"
},
{
"parameters": {
"method": "POST",
"url": "https://supabase.megyk.com/rest/v1/rpc//match_faqs_json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "query_embedding",
"value": "={{ $json.data[0].embedding }}"
},
{
"name": "match_count",
"value": "5"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": true,
"responseFormat": "json"
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2288,
-16
],
"id": "975f770e-1c1f-4b4a-a237-9228b0e99953",
"name": "Supabase RPC Retrieval",
"alwaysOutputData": false,
"executeOnce": false,
"credentials": {
"httpCustomAuth": {
"id": "qTH8XGwb9fhDknYB",
"name": "Custom Supabase Auth"
},
"supabaseApi": {
"id": "ocLTr2Ljnx729d0p",
"name": "Supabase account 2"
}
}
},
{
"parameters": {
"content": "## Retrieval ( Auto Q & A Flow )\n### Retrieval (Auto Q & A Flow)\n\nFlow: \n1. **Trigger:** Gmail node receives new survey response with user questions. \n2. **Parse Questions:** Extract raw text of each user question. \n3. **OpenAI Embeddings:** \n - Model: `text-embedding-3-small` (1536-dim) \n - Input: `{{$json[\"question\"]}}` (user\u2019s question in German) \n - Output: `{{$json[\"data\"][0][\"embedding\"]}}` \n4. **Supabase RPC Retrieval:** \n - Calls `match_faqs_json(query_embedding, match_count=5)` \n - Returns top FAQ matches with similarity scores. \n5. **AI Reply (next step):** \n - Pass best match into OpenAI Chat node \n - Draft personalized auto-reply email using FAQ answer as context \n - Format into Gmail/Bitrix email template \n\n",
"height": 912,
"width": 1408,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1088,
-464
],
"id": "de68b760-5b4d-470e-b296-db392d007c8a",
"name": "Sticky Note1"
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/embeddings",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "text-embedding-3-small"
},
{
"name": "input",
"value": "={{ $json.json.question }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2064,
-16
],
"id": "ebf84add-dfbb-458c-9415-8a043c40d877",
"name": "Question Embedding",
"credentials": {
"httpBearerAuth": {
"id": "t0h6OX63Uc1M41Fp",
"name": "OpenAi Megyk Tests"
}
}
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1RlfPHGwn5uZ1ucIdLnk_6ZFPJPfhTpa8uQwsYuJCYug",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": 651789693,
"mode": "list",
"cachedResultName": "Copy of Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1RlfPHGwn5uZ1ucIdLnk_6ZFPJPfhTpa8uQwsYuJCYug/edit#gid=651789693"
}
},
"type": "n8n-nodes-base.evaluationTrigger",
"typeVersion": 4.6,
"position": [
624,
432
],
"id": "71cc0514-e666-4a83-893e-8a15946874cc",
"name": "When fetching a dataset row",
"credentials": {
"googleSheetsOAuth2Api": {
"id": "qJ5ck4AVx9Jx37Fr",
"name": "Google Sheets account"
}
}
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1RlfPHGwn5uZ1ucIdLnk_6ZFPJPfhTpa8uQwsYuJCYug",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": 651789693,
"mode": "list",
"cachedResultName": "Copy of Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1RlfPHGwn5uZ1ucIdLnk_6ZFPJPfhTpa8uQwsYuJCYug/edit#gid=651789693"
},
"outputs": {
"values": [
{
"outputName": "answer_correctness",
"outputValue": "={{ $json.Correctness }}"
},
{
"outputName": "generated_answer",
"outputValue": "={{ $('Generate Email Reply Segment').item.json.message.content }}"
},
{
"outputName": "result",
"outputValue": "={{ ($json.Correctness >=2)? 'Pass' : 'Fail' }}"
},
{
"outputName": "actual_faq_match",
"outputValue": "={{ $('Supabase RPC Retrieval').item.json.body[0].id }}"
}
]
}
},
"type": "n8n-nodes-base.evaluation",
"typeVersion": 4.7,
"position": [
1536,
512
],
"id": "337fb1b5-c32d-4a64-8e74-0ee42d8c8132",
"name": "Evaluation",
"credentials": {
"googleSheetsOAuth2Api": {
"id": "qJ5ck4AVx9Jx37Fr",
"name": "Google Sheets account"
}
}
},
{
"parameters": {
"operation": "setMetrics",
"expectedAnswer": "={{ $('Supabase RPC Retrieval').item.json.body[0].answer }}",
"actualAnswer": "={{ $json.message.content }}",
"options": {}
},
"type": "n8n-nodes-base.evaluation",
"typeVersion": 4.7,
"position": [
1184,
512
],
"id": "b3dc6f0a-8e28-4525-87a0-ad0faaf08f9e",
"name": "Evaluation1"
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
1248,
736
],
"id": "11ed9aac-dc7e-4f06-a480-f2a5e22b18bb",
"name": "OpenAI Chat Model",
"credentials": {
"openAiApi": {
"id": "BRRf66J5aSwt4UDP",
"name": "OpenAi account"
}
}
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "openrouter/free",
"mode": "list",
"cachedResultName": "OPENROUTER/FREE"
},
"messages": {
"values": [
{
"content": "=FAQ Matches:\n{{ JSON.stringify($json.body) }}\n\nHuman-approved response for this matched FAQ, if available:\n{{$json.body[0].approved_response || 'No human-approved response available.' }}\n\nUser Question:\n{{ $('Split Out').item.json.json.question }}\n\nInstructions:\nAnswer the user question using the retrieved FAQ answers.\n\nIf a human-approved response is available, use it as the preferred answer pattern and adapt it naturally to the current question.\n\n{{\n $itemIndex === $items().length - 1\n ? \"- Answer in a few sentences, and answer in a way that elicits curiosity from the user to take the next step and schedule an appointment. Build curiosity and anticipation within the answer so the participant is interested in hearing more and discussing it personally.\"\n : \"\"\n}}"
},
{
"content": "You are a helpful support assistant for the Deutsches Edelsteinhaus FAQ system.\n\nAlways answer in German unless the customer explicitly asks for English.\n\nUse the retrieved FAQ answers as your source of truth. You may rephrase, clarify, or combine them for a natural reply, but you must not invent facts beyond what is provided.\n\nIf a human-approved response is provided, treat it as preferred guidance for this matched FAQ. Use it to align tone, structure, wording, and business positioning. Stay close to it when appropriate, but adapt naturally to the current customer question.\n\nIf the human-approved response conflicts with the retrieved FAQ answers, prefer the retrieved FAQ answers.\n\nIf the FAQ answers are incomplete, politely acknowledge the limits and keep the answer conservative.\n\nKeep a professional but warm tone suitable for email replies to potential investors.\n\nFormat your answer as a segment of an email body with no greeting and no closing signature. The answer segments will be joined together later.",
"role": "system"
}
]
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [
1280,
224
],
"id": "4fc5f1d9-4770-41aa-9f20-bbb186c03db3",
"name": "Generate Email Reply Segment",
"credentials": {
"openAiApi": {
"id": "nYUZBGx2UZRpW2Ia",
"name": "OpenRouter Account"
}
}
},
{
"parameters": {
"fieldToSplitOut": "questions",
"options": {}
},
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [
1856,
-16
],
"id": "70a7b9ea-11da-4987-bac7-9230262e8aa6",
"name": "Split Out"
},
{
"parameters": {
"operation": "appendOrUpdate",
"documentId": {
"__rl": true,
"value": "10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50",
"mode": "list",
"cachedResultName": "FAQ Responder Approvals",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/10SIcM35jm94h3eTcdFzzLfxM4e-9_Enseu9R6oYAE50/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Question": "={{ $('Split Out').item.json.json.question }}",
"date": "={{ $now.format('yyyy-MM-dd') }}",
"Agent Response": "={{ $json.message.content }}",
"Reply To": "={{ $('Parse Questions').item.json.replyTo }}",
"match_score": "={{ $('Supabase RPC Retrieval').item.json.body.first().similarity }}",
"match_uuid": "={{ $('Supabase RPC Retrieval').item.json.body.first().id}}",
"thread_id": "={{ $('Gmail Trigger').item.json.threadId }}",
"Reply To Name": "={{ $('Parse Questions').item.json.replyToName.fullname }}"
},
"matchingColumns": [
"Question"
],
"schema": [
{
"id": "date",
"displayName": "date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "thread_id",
"displayName": "thread_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "match_uuid",
"displayName": "match_uuid",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "match_score",
"displayName": "match_score",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Reply To",
"displayName": "Reply To",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Reply To Name",
"displayName": "Reply To Name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Question",
"displayName": "Question",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Agent Response",
"displayName": "Agent Response",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Approved",
"displayName": "Approved",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "Sent",
"displayName": "Sent",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
1648,
224
],
"id": "099f0d19-927b-4d0d-90d6-4a1ea0a18cb0",
"name": "Append or update row in sheet",
"credentials": {
"googleSheetsOAuth2Api": {
"id": "qJ5ck4AVx9Jx37Fr",
"name": "Google Sheets account"
}
}
}
],
"connections": {
"Manual Trigger": {
"main": [
[
{
"node": "Load SOP",
"type": "main",
"index": 0
}
]
]
},
"Load SOP": {
"main": [
[
{
"node": "Extract FAQs",
"type": "main",
"index": 0
}
]
]
},
"Extract FAQs": {
"main": [
[
{
"node": "Message a model",
"type": "main",
"index": 0
}
]
]
},
"Message a model": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Filter",
"type": "main",
"index": 0
}
]
]
},
"Filter": {
"main": [
[
{
"node": "Generate Embeddings",
"type": "main",
"index": 0
}
]
]
},
"Generate Embeddings": {
"main": [
[
{
"node": "Create a row",
"type": "main",
"index": 0
}
]
]
},
"Gmail Trigger": {
"main": [
[
{
"node": "Parse Questions",
"type": "main",
"index": 0
}
]
]
},
"Parse Questions": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
},
"Supabase RPC Retrieval": {
"main": [
[
{
"node": "Generate Email Reply Segment",
"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.
gmailOAuth2googleDocsOAuth2ApigoogleSheetsOAuth2ApihttpBearerAuthhttpCustomAuthopenAiApisupabaseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Self-Learning FAQs RAG. Uses googleDocs, openAi, supabase, httpRequest. Event-driven trigger; 21 nodes.
Source: https://github.com/adamhaley/megyk-automations/blob/67dcb104ded1850ab52e7fb6ca82be3c5eb810aa/workflows/Self-Learning_FAQs_RAG.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.
FAQs Embeddings. Uses googleDocs, openAi, supabase, httpRequest. Event-driven trigger; 35 nodes.
This n8n template demonstrates how to automate email classification, labeling, draft generation, and logging using Gmail, OpenAI, and Google Sheets. Use cases include customer support management, sale
FAQs Embeddings. Uses googleDocs, openAi, supabase, httpRequest. Event-driven trigger; 35 nodes.
Detects new unread Gmail messages Extracts sender name for personalized replies Classifies the email into one of four categories Applies the correct Gmail label and either sends an auto-reply, creates
This section automates Gmail message handling through AI-powered classification and response. Using the LangChain Text Classifier, incoming emails are analyzed and sorted into four categories — High P