AutomationFlowsAI & RAG › Generate FAQ Embeddings with OpenAI & Supabase

Generate FAQ Embeddings with OpenAI & Supabase

Original n8n title: Faqs Embeddings

FAQs Embeddings. Uses googleDocs, openAi, supabase, httpRequest. Event-driven trigger; 35 nodes.

Event trigger★★★★★ complexityAI-powered35 nodesGoogle DocsOpenAISupabaseHTTP RequestGmail TriggerEvaluation TriggerEvaluationOpenAI Chat
AI & RAG Trigger: Event Nodes: 35 Complexity: ★★★★★ AI nodes: yes Added:
Generate FAQ Embeddings with OpenAI & Supabase — n8n workflow card showing Google Docs, OpenAI, Supabase integration

This workflow follows the Gmail → Gmail Trigger recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "updatedAt": "2026-05-12T03:10:55.960Z",
  "createdAt": "2025-09-11T19:49:07.661Z",
  "id": "lLmdtJWpmMd86l0W",
  "name": "FAQs Embeddings",
  "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": [
        1232,
        -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": [
        256,
        928
      ],
      "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": [
        544,
        1088
      ],
      "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": [
        240,
        1088
      ],
      "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": [
        288,
        1264
      ],
      "id": "11ed9aac-dc7e-4f06-a480-f2a5e22b18bb",
      "name": "OpenAI Chat Model",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "gpt-4.1-mini",
          "mode": "list",
          "cachedResultName": "GPT-4.1-MINI"
        },
        "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": [
        1232,
        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": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.body }}",
        "options": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        2304,
        624
      ],
      "id": "ef672b2a-fe04-409e-9181-d008643d44d2",
      "name": "Send a message",
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "get",
        "tableId": "email_templates",
        "filters": {
          "conditions": [
            {
              "keyName": "id",
              "keyValue": "1"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1712,
        624
      ],
      "id": "28068b06-5745-4e3a-a4b0-fda02998e707",
      "name": "Load Email Template",
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\n\nconst qaPairs = items.map((item, index) => {\n  const question = item.json.Question || \"\";\n  const answer = item.json[\"Column 1\"] || item.json[\"Agent Response\"] || \"\";\n\n  return `<p><strong>Ihre Frage:</strong> ${question}</p>\n<p>${answer}</p>`;\n});\n\nconst body = qaPairs.join(\"\\n\\n\");\n\nreturn [\n  {\n    json: {\n      thread_id: items[0]?.json.thread_id || \"\",\n      reply_to: items[0]?.json[\"Reply To\"] || \"\",\n      body,\n      qa_count: qaPairs.length,\n    },\n  },\n];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1520,
        624
      ],
      "id": "1f6f45db-12a3-4687-9fdf-1b3965214048",
      "name": "Join Q&A"
    },
    {
      "parameters": {
        "jsCode": "let template = $('Load Email Template').first().json.body;\nlet greeting = $input.first().json.message.content;\nlet questions = $('Join Q&A').first().json.body;\n\ntemplate = template.replace('{{greeting}}', greeting);\nlet emailbody = template.replace('{{questions}}', questions);\n\nconst replyTo = $('Join Q&A').first().json.reply_to;\nconst participantName = $('Google Sheets Trigger1').first().json[\"Reply To Name\"] || '';\n\nreturn {\n  to: replyTo,\n  participant_email: replyTo,\n  participant_name: participantName,\n  subject: `Deutsches Edelsteinhaus Sachwerte - Ihre Webinarfrage`,\n  body: emailbody\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2080,
        624
      ],
      "id": "8b053cba-fa88-4785-a334-5aabf7fd3115",
      "name": "Replace Content"
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "gpt-4.1-mini",
          "mode": "list",
          "cachedResultName": "GPT-4.1-MINI"
        },
        "messages": {
          "values": [
            {
              "content": "=You are an expert email salutation generator. Your role is to take an incoming name, determine if it is male or female, and generate a formal greeting in german to be placed at the top of an email. You will only generate the greeting, nothing else. No punctuation necessary.\n\nExamples:\n\"Sehr geehrter Herr Foth\"\n\"Sehr geehrte Frau Foth\"\n\nThe incoming name is: {{ $('Google Sheets Trigger1').item.json['Reply To Name'] }}"
            }
          ]
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8,
      "position": [
        1552,
        880
      ],
      "id": "5f3b4029-4391-4164-ac5f-0fb59fda4ef2",
      "name": "Generate Greeting",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "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
            }
          ],
          "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>"
        }
      }
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyHour"
            }
          ]
        },
        "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"
        },
        "event": "rowUpdate",
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "typeVersion": 1,
      "position": [
        304,
        624
      ],
      "id": "548fb7a6-0028-4a4a-8bea-e179d2e5c566",
      "name": "Google Sheets Trigger",
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "97501650-9141-437f-b85a-6c6cf4f48144",
              "leftValue": "={{ $json.Approved.toBoolean() }}",
              "rightValue": "true",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.3,
      "position": [
        528,
        624
      ],
      "id": "37217c71-42ee-4dca-b120-0e88c4e097c7",
      "name": "Filter1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://supabase.megyk.com/rest/v1/rpc/upsert_faq_approved_response",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"p_faq_id\": \"{{ $json.match_uuid }}\",\n  \"p_approved_response\": {{ JSON.stringify($json['Agent Response'])  }}\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        752,
        624
      ],
      "id": "9a93fa19-3e1a-46a0-9e3f-83ea344af8a5",
      "name": "Upsert FAQ Approved Answers",
      "executeOnce": false,
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## Test for all question/answers approved for that given webinar/user",
        "height": 304,
        "width": 1408
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1088,
        528
      ],
      "id": "eaa1e299-f499-4db3-851b-bf311662347b",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "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"
        },
        "event": "rowUpdate",
        "options": {
          "columnsToWatch": [
            "Approved"
          ]
        }
      },
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "typeVersion": 1,
      "position": [
        720,
        880
      ],
      "id": "933e1a3f-ac75-4f2c-8561-71372a61ccc4",
      "name": "Google Sheets Trigger1",
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## Send Approved Answers to Supabase",
        "height": 272,
        "width": 1008,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -80,
        528
      ],
      "id": "fe9a82d7-ad16-4033-a01f-b00ef6bd5bd1",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\n\n// Normalize Approved values because Google Sheets may return true, \"true\", \"\", etc.\nfunction isApproved(value) {\n  return value === true || String(value).toLowerCase().trim() === 'true';\n}\n\n// Group items by thread_id\nconst threads = {};\n\nfor (const item of items) {\n  const threadId = item.json.thread_id;\n\n  if (!threads[threadId]) {\n    threads[threadId] = [];\n  }\n\n  threads[threadId].push(item);\n}\n\n// Only allow items from threads where every item is Approved === true\nconst approvedItems = [];\n\nfor (const threadId of Object.keys(threads)) {\n  const threadItems = threads[threadId];\n\n  const allApproved = threadItems.every(item => {\n    return isApproved(item.json.Approved);\n  });\n\n  if (allApproved) {\n    approvedItems.push(...threadItems);\n  }\n}\n\nreturn approvedItems;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1296,
        624
      ],
      "id": "cf603461-ca5f-4ae0-a398-70a1bec78163",
      "name": "Filter all Approved"
    },
    {
      "parameters": {
        "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"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "thread_id",
              "lookupValue": "={{ $json.thread_id }}"
            },
            {
              "lookupColumn": "Sent",
              "lookupValue": "false"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        944,
        880
      ],
      "id": "43d4e055-d38e-4b3d-9200-3778e91ca3a7",
      "name": "Get row(s) in sheet",
      "executeOnce": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "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": {
            "thread_id": "={{ $('Get row(s) in sheet').item.json.thread_id }}",
            "Sent": "true"
          },
          "matchingColumns": [
            "thread_id"
          ],
          "schema": [
            {
              "id": "date",
              "displayName": "date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": 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": true
            },
            {
              "id": "match_score",
              "displayName": "match_score",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Reply To",
              "displayName": "Reply To",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Reply To Name",
              "displayName": "Reply To Name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Question",
              "displayName": "Question",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Agent Response",
              "displayName": "Agent Response",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": 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
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2304,
        880
      ],
      "id": "209b61b4-e494-4f9c-aeb2-840a7514d694",
      "name": "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
          }
        ]
      ]
    },
    "Join Q&A": {
      "main": [
        [
          {
            "node": "Load Email Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Email Template": {
      "main": [
        [
          {
            "node": "Generate Greeting",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Replace Content": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Greeting": {
      "main": [
        [
          {
            "node": "Replace Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append or update row in sheet": {
      "main": [
        []
      ]
    },
    "Google Sheets Trigger": {
      "main": [
        [
          {
            "node": "Filter1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter1": {
      "main": [
        [
          {
            "node": "Upsert FAQ Approved Answers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets Trigger1": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter all Approved": {
      "main": [
        [
          {
            "node": "Join Q&A",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet": {
      "main": [
        [
          {
            "node": "Filter all Approved",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Update row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "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": "109fb1ea-a13a-454f-a20b-2aca0ecf30a0",
  "activeVersionId": "109fb1ea-a13a-454f-a20b-2aca0ecf30a0",
  "triggerCount": 3,
  "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-12T03:11:01.000Z",
    "createdAt": "2026-05-12T03:10:55.964Z",
    "versionId": "109fb1ea-a13a-454f-a20b-2aca0ecf30a0",
    "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": [
          1232,
          -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-f08485cb6

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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

FAQs Embeddings. Uses googleDocs, openAi, supabase, httpRequest. Event-driven trigger; 35 nodes.

Source: https://github.com/adamhaley/megyk-automations/blob/e97e81bdcd7886cbc5723dc1f539b1bffa41c6de/workflows/FAQs_Embeddings.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This advanced n8n workflow automates the full lead enrichment, qualification, and personalized outreach process tailored specifically for the B2B real estate sector. Integrating top platforms like Api

N8N Nodes Fillout, OpenAI Chat, Pinecone Vector Store +11
AI & RAG

This n8n template automatically classifies incoming emails (Sales, Support, Internal, Finance, Promotions) and routes them to a dedicated OpenAI LLM Agent for processing. Depending on the category, th

OpenAI, Gmail, Text Classifier +16
AI & RAG

FAQs Embeddings. Uses googleDocs, openAi, supabase, httpRequest. Event-driven trigger; 35 nodes.

Google Docs, OpenAI, Supabase +8
AI & RAG

This workflow automates patient communication for medical clinics using the WhatsApp Business API. It supports appointment booking, rescheduling, service inquiries, follow-ups, and document submission

Google Sheets, Data Table, Data Table Tool +12
AI & RAG

WooriFisa 최종. Uses memoryMongoDbChat, agent, httpRequest, documentDefaultDataLoader. Scheduled trigger; 68 nodes.

Memory Mongo Db Chat, Agent, HTTP Request +14