AutomationFlowsAI & RAG › Automated Email Responses with Gpt-4o & Supabase Conversation Memory

Automated Email Responses with Gpt-4o & Supabase Conversation Memory

ByLukman @lukmanabdh on n8n.io

Ideal for businesses that receive frequent inquiries about products or services and want to automate responses, freeing up time to focus on core operations. Polls your inbox for new incoming emails Cleans HTML to reduce token usage and filters spam Categorizes emails for better…

Event trigger★★★★★ complexityAI-powered32 nodesOpenAI EmbeddingsMicrosoft Outlook TriggerOpenAIOutput Parser StructuredOpenAI ChatPostgresAgentSupabase Vector Store
AI & RAG Trigger: Event Nodes: 32 Complexity: ★★★★★ AI nodes: yes Added:

This workflow corresponds to n8n.io template #9970 — we link there as the canonical source.

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

The workflow JSON

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

Download .json
{
  "nodes": [
    {
      "id": "2ea256d3-ba6f-4150-8f2b-e157b531967e",
      "name": "Embeddings OpenAI1",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "position": [
        3184,
        528
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "8aa40c2d-d7db-4c09-9d57-ead456da3a19",
      "name": "Embeddings OpenAI2",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "position": [
        2880,
        528
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ddbda963-adae-4555-a2df-a37e96a45de2",
      "name": "Microsoft Outlook Trigger",
      "type": "n8n-nodes-base.microsoftOutlookTrigger",
      "position": [
        1024,
        224
      ],
      "parameters": {
        "output": "raw",
        "filters": {
          "readStatus": "unread",
          "hasAttachments": false,
          "foldersToInclude": [
            "AQMkADAwATMwMAItNDc5YS02YzcyLTAwAi0wMAoALgAAAyr74A1aBA5FmGCW-N3seyYBAK7gOo5dtNlAihU21SrvhjMAAAIBDAAAAA=="
          ]
        },
        "options": {
          "attachmentsPrefix": "attachment",
          "downloadAttachments": false
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        }
      },
      "credentials": {
        "microsoftOutlookOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bf25d741-540a-4c47-a656-477c150dfa0f",
      "name": "Clean HTML",
      "type": "n8n-nodes-base.code",
      "position": [
        1520,
        224
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\nreturn items.map(item => {\n  let html = item.json.body.content;\n  \n  // FIRST: Remove quoted blocks while HTML is still structured\n  html = html\n    .replace(/<div class=\"gmail_quote\">[\\s\\S]*?<\\/div>/gi, '')\n    .replace(/<blockquote[\\s\\S]*?<\\/blockquote>/gi, '')\n    .replace(/<div[^>]*id=\"divRplyFwdMsg\"[\\s\\S]*?<\\/div>/gi, '')\n    .replace(/<hr[^>]*>[\\s\\S]*$/gi, '');\n  \n  // THEN: Strip all HTML\n  let text = html\n    .replace(/<style[^>]*>.*?<\\/style>/gis, '')\n    .replace(/<script[^>]*>.*?<\\/script>/gis, '')\n    .replace(/<[^>]+>/g, '')\n    .replace(/&nbsp;/g, ' ')\n    .replace(/&quot;/g, '\"')\n    .replace(/&amp;/g, '&')\n    .replace(/&[a-z]+;/gi, ' ')\n    .replace(/\\s+/g, ' ')\n    .trim();\n  \n  // FINALLY: Regex fallback for plain text quotes\n  const quotePatterns = [\n    /On\\s+.+?wrote:/i,\n    /From:\\s*.+?Sent:/is,\n    /_{5,}/,\n    /-{5,}\\s*Original Message\\s*-{5,}/i\n  ];\n  \n  let splitIndex = text.length;\n  for (const pattern of quotePatterns) {\n    const match = text.search(pattern);\n    if (match !== -1 && match < splitIndex) {\n      splitIndex = match;\n    }\n  }\n  \n  text = text.substring(0, splitIndex).trim();\n  \n  return {\n    json: {\n      ...item.json,\n      cleanBody: text\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "0154c66d-cedc-4021-b895-ff00e91fc524",
      "name": "Categorize",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2112,
        224
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=Here are the email details:\nFrom Email: {{ $('Clean HTML').item.json.from.emailAddress.address }}\nFrom Name: {{ $('Clean HTML').item.json.from.emailAddress.name }}\nSubject: {{ $('Clean HTML').item.json.subject }}\nBody: {{ $('Clean HTML').item.json.cleanBody }}\n\n"
            },
            {
              "role": "system",
              "content": "=You are an email classifier for a [COMPANY].\n\n# Task:\n1. Categorize the incoming email based on the provided category list\n2. If the email strongly fits an existing category, use it\n3. If the email would be \"Other\" but represents a meaningful, recurring business pattern, create a new specific category\n4. Output in structured JSON\n\n# Current Categories:\n{{ $json.category }}\n\n# Spam Detection (Always check first):\n- Generic greetings with urgent money requests\n- Cryptocurrency, loans, prizes, inheritance scams\n- Suspicious links or poor grammar with urgency\n- Unsolicited financial offers\n\nIf spam detected, immediately output: {\"category\": \"SPAM\"}\n\n# Categorization Logic:\n1. Check if email clearly matches an existing category\n2. If yes: Use that category\n3. If no strong match: Evaluate if a NEW category would be beneficial\n\n# New Category Criteria (All must be true):\n- Represents a distinct, recurring business function for a construction company\n- Will likely receive multiple similar emails per month\n- Requires different handling than existing categories\n- Category name is specific and action-oriented\n- Not already covered by existing categories\n\n# Invalid New Category Examples:\n- \"Important\"\n- \"Urgent\"\n- \"Miscellaneous\"\n- \"Random Emails\"\n- \"Needs Review\"\n- \"Follow Up\"\n\n# Legitimate Business Patterns:\n- Specific project references\n- Construction/renovation terminology\n- Professional supplier/customer correspondence\n- Job applications with CV\n\n# Output Format (JSON only):\n{\n  \"category\": \"string\"\n}\n\n# Rules:\n- SPAM always uses existing \"SPAM\" category\n- Only create new categories for legitimate, recurring business needs\n- If uncertain, use the closest existing category\n- New category names: 1-3 words, title case, construction-relevant\n- Only respond in valid JSON format"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "4e661d6c-b4f5-43f2-bc74-58ea5dc8b349",
      "name": "JSON",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        3152,
        144
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"body\": \"full email body with <br> tags for line breaks\",\n  \"forward\": true\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "3ea7fe84-95e1-40ae-b992-11c637b85b55",
      "name": "4o",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2976,
        144
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "gpt-4o"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7beff76f-0000-4abf-b676-e9989d1def42",
      "name": "Conversation Retrieval",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2640,
        224
      ],
      "parameters": {
        "query": "SELECT subject, category, content, reply, date\nFROM emailreplies\nWHERE conversation_id = '{{ $('Clean HTML').item.json.conversationId }}';\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "typeVersion": 2.6,
      "alwaysOutputData": true
    },
    {
      "id": "63ac0d0b-5167-466d-8814-6cc4933b1390",
      "name": "Loop Over Items1",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1280,
        224
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "77ac6486-243d-41b4-9822-81d79a219f82",
      "name": "Aggregate1",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        1904,
        224
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "category"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "17e15174-3f73-4e93-9610-82891b90ae0a",
      "name": "Spam Filter",
      "type": "n8n-nodes-base.if",
      "position": [
        2432,
        224
      ],
      "parameters": {
        "options": {
          "ignoreCase": true
        },
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "99049b97-ec32-4af1-8f46-7362f431ce0d",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.message.content.category }}",
              "rightValue": "spam"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0fba875c-61ae-4779-861c-bf1d0c76a820",
      "name": "Email Manager",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2992,
        224
      ],
      "parameters": {
        "text": "=From: {{ $('Clean HTML').first().json.from.emailAddress.address }}\nName: {{ $('Clean HTML').first().json.from.emailAddress.name }}\nEmail Content: {{ $('Clean HTML').first().json.cleanBody }}\nCategory: {{ $('Categorize').item.json.message.content.category }}\nHas attachment: {{ $('Clean HTML').item.json.hasAttachments }}\nConversation History: {{ $json.conversationHistory }}\n",
        "options": {
          "systemMessage": "=You are an email assistant for a [COMPANY] drafting professional email replies.\n\n# Context Provided\n- From/Name: Sender details\n- Email Content: The message\n- Category: Email category \n- Has attachment: true/false\n- Conversation History: Previous exchanges (if any)\n\n# Process\n1. Check if email has attachment:\n   - IF Has attachment = true: Skip to step 4 (draft acknowledgment + set forward=true)\n\n2. Use 'FAQ DB' tool to search for relevant answers\n   - Evaluate results: Do they adequately answer the sender's question?\n   - Are the results on-topic and helpful?\n\n3. Use 'Email Template DB' tool to find appropriate reply format\n   - First search: \"Category: {{ $('Categorize').item.json.message.content.category }}. [relevant search terms]\"\n   - If results seem off-topic or unhelpful: Retry without category prefix\n\n4. Determine action:\n   - Can answer with confidence: Draft full reply, set forward=false\n   - FAQ results are off-topic/incomplete: Draft placeholder, set forward=true\n   - Email is a complaint/urgent/complex: Draft placeholder, set forward=true\n   - Has attachment: Draft acknowledgment, set forward=true\n\n# Reply Templates\n- Full answer: Use FAQ information + Email Template style, personalized with sender's name\n- Placeholder (when forwarding):\n\"Dear [Name],<br><br>Thank you for your email. Our team will review your [request/question/attachment] and respond within 24 hours.<br><br>Best regards\"\n\n# Output Format (strict JSON only)\n{\n  \"body\": \"full email body with <br> tags for line breaks\",\n  \"forward\": true or false\n}\n\n# Rules\n- ALWAYS use both tools unless attachment is present\n- ALWAYS address sender by name in email body\n- Evaluate tool results based on relevance to the question, not scores\n- Set forward=true if: attachment present, FAQ unhelpful, or email requires human attention\n- Output valid JSON only\n- Use <br> tags for line breaks."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "5e2a8030-3705-44fe-9530-f2c79cccde0f",
      "name": "Format",
      "type": "n8n-nodes-base.code",
      "position": [
        2800,
        224
      ],
      "parameters": {
        "jsCode": "// Get all input items\nconst items = $input.all();\n\n// Sort by date (earliest first)\nconst sortedItems = items.sort((a, b) => {\n  const dateA = new Date(a.json.date);\n  const dateB = new Date(b.json.date);\n  return dateA - dateB;\n});\n\n// Build the formatted string\nlet output = '';\n\n// Add date, body and reply for each item\nsortedItems.forEach((item, index) => {\n  const data = item.json;\n  output += `Date ${index + 1}: ${data.date || ''}\\n`;\n  output += `Body ${index + 1}: ${data.content || ''}\\n`;\n  output += `Reply ${index + 1}: ${data.reply || ''}\\n`;\n});\n\n// Return the formatted string\nreturn [{ json: { conversationHistory: output } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "a23f1876-6ef1-4678-999b-1d39c969676c",
      "name": "Email Template DB",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
      "position": [
        3184,
        448
      ],
      "parameters": {
        "mode": "retrieve-as-tool",
        "options": {
          "queryName": "match_emailreplies"
        },
        "tableName": {
          "__rl": true,
          "mode": "list",
          "value": "emailreplies",
          "cachedResultName": "emailreplies"
        },
        "toolDescription": "Use this to find the most relevant email reply templates by vector similarity in the Email Reply Template table."
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "ddfbc5a8-9449-4bde-87ea-9a807d843e21",
      "name": "FAQ DB",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
      "position": [
        2880,
        448
      ],
      "parameters": {
        "mode": "retrieve-as-tool",
        "options": {
          "queryName": "match_faq"
        },
        "tableName": {
          "__rl": true,
          "mode": "list",
          "value": "faq",
          "cachedResultName": "faq"
        },
        "toolDescription": "Use this to find the most relevant FAQ by vector similarity in the FAQ table."
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "8f31665f-f493-4ad6-b14e-9b6bb6e49cdd",
      "name": "Reply",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        3584,
        304
      ],
      "parameters": {
        "message": "={{ $json.output.body }}",
        "options": {},
        "messageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Clean HTML').item.json.id }}"
        },
        "operation": "reply"
      },
      "credentials": {
        "microsoftOutlookOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "9dacab9f-27f1-4fe8-a826-3957d75207d7",
      "name": "Reply1",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        3584,
        128
      ],
      "parameters": {
        "message": "={{ $json.output.body }}",
        "options": {},
        "messageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Clean HTML').item.json.id }}"
        },
        "operation": "reply"
      },
      "credentials": {
        "microsoftOutlookOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "e0ce9f54-37ef-42b4-b807-60b8f6de35a8",
      "name": "Forward?",
      "type": "n8n-nodes-base.if",
      "position": [
        3312,
        224
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0abfde8c-38e2-48fe-918a-f10f962a2d63",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.output.forward }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "1a6ce559-bd71-4dd0-b1cb-9dd40efdb3e0",
      "name": "Retrieve Categories",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1712,
        224
      ],
      "parameters": {
        "query": "SELECT DISTINCT category\nFROM emailreplies;",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.6
    },
    {
      "id": "cbd88406-110c-4dd8-8d77-2a48a1424e18",
      "name": "Execute Workflow",
      "type": "n8n-nodes-base.executeWorkflow",
      "position": [
        4096,
        224
      ],
      "parameters": {
        "options": {},
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "l6VbNoViT2nEeTmN"
        },
        "workflowInputs": {
          "value": {
            "body": "={{ $('Clean HTML').item.json.cleanBody }}",
            "date": "={{ $('Clean HTML').item.json.receivedDateTime }}",
            "reply": "={{ $('Email Manager').item.json.output.body }}",
            "subject": "={{ $('Clean HTML').item.json.subject }}",
            "category": "={{ $('Categorize').item.json.message.content.category }}",
            "conversationID": "={{ $('Clean HTML').item.json.conversationId }}"
          },
          "schema": [
            {
              "id": "subject",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "subject",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "body",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "body",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "category",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reply",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "reply",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "conversationID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "conversationID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "d0c6e016-a059-4f12-bb17-5aeb685c4a2f",
      "name": "Outlook Forward",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3792,
        128
      ],
      "parameters": {
        "url": "=https://graph.microsoft.com/v1.0/me/messages/{{ $('Clean HTML').item.json.id }}/forward",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n     \"toRecipients\": [\n       {\n         \"emailAddress\": {\n           \"address\": \"\"\n         }\n       }\n     ],\n     \"comment\": \"Please review the following email\"\n   }",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "microsoftOutlookOAuth2Api"
      },
      "credentials": {
        "microsoftOutlookOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e7d9b7a8-ba35-4734-b212-6eb64d23f265",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        -16
      ],
      "parameters": {
        "width": 400,
        "height": 560,
        "content": "# Automation Overview\n\n1. Polls inbox for incoming emails\n2. Cleans HTML tags to reduce token bloating\n3. Identifies existing categories the emails could fall under\n4. LLM categorizes it and identifies spam\n5. Filters out spam emails\n6. Identifies whether or not an existing conversation exists\n7. AI Agent uses conversation history (if available) for context and uses FAQ documents ingested in Supabase to answer incoming questions\n8. If it can confidently answer, it will use an email template to structure it's reply\n9. If it can't answer using the FAQ documents, it will flag for human review.\n10. Every email gets ingested into Supabase to build a database of conversation history"
      },
      "typeVersion": 1
    },
    {
      "id": "67b4bb5a-0e42-4191-97d7-db66a033c7e7",
      "name": "When Executed by Another Workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        1040,
        960
      ],
      "parameters": {
        "inputSource": "jsonExample",
        "jsonExample": "{\n  \"subject\": \"\",\n  \"body\": \"\",\n  \"category\": \"\",\n  \"reply\": \"\",\n  \"date\": \"\",\n  \"conversationID\": \"\"\n}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "e3de77e5-f79f-4904-b43c-2f8ba4a55f45",
      "name": "Supabase Vector Store4",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
      "position": [
        1296,
        960
      ],
      "parameters": {
        "mode": "insert",
        "options": {
          "queryName": "match_emailreplies"
        },
        "tableName": {
          "__rl": true,
          "mode": "list",
          "value": "emailreplies",
          "cachedResultName": "emailreplies"
        }
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "49825c43-d994-45a9-92e7-4f8b0ef903b6",
      "name": "Embeddings OpenAI6",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "position": [
        1296,
        1056
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "8499257a-c029-4a1b-81e1-73d8ce8694cf",
      "name": "Default Data Loader3",
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "position": [
        1392,
        1104
      ],
      "parameters": {
        "options": {},
        "jsonData": "=\"subject\": \"{{ $json.subject }}\",\n\"body\": \"{{ $json.body }}\",\n\"category\": \"{{ $json.category }}\"",
        "jsonMode": "expressionData",
        "textSplittingMode": "custom"
      },
      "typeVersion": 1.1
    },
    {
      "id": "8defcd27-ca6a-4734-890a-9b7abb18430e",
      "name": "Recursive Character Text Splitter3",
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "position": [
        1392,
        1184
      ],
      "parameters": {
        "options": {},
        "chunkSize": 10000
      },
      "typeVersion": 1
    },
    {
      "id": "24db4506-fdff-4710-b06d-0269dfa69d2c",
      "name": "Update a row2",
      "type": "n8n-nodes-base.supabase",
      "position": [
        1648,
        960
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "content",
              "keyValue": "={{ $json.pageContent }}",
              "condition": "eq"
            }
          ]
        },
        "tableId": "emailreplies",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "category",
              "fieldValue": "={{ $('When Executed by Another Workflow').item.json.category }}"
            },
            {
              "fieldId": "flag",
              "fieldValue": "FALSE"
            },
            {
              "fieldId": "subject",
              "fieldValue": "={{ $('When Executed by Another Workflow').item.json.subject }}"
            },
            {
              "fieldId": "conversation_id",
              "fieldValue": "={{ $('When Executed by Another Workflow').item.json.conversationID }}"
            },
            {
              "fieldId": "date",
              "fieldValue": "={{ $('When Executed by Another Workflow').item.json.date }}"
            },
            {
              "fieldId": "reply",
              "fieldValue": "={{ $('When Executed by Another Workflow').item.json.reply }}"
            }
          ]
        },
        "matchType": "allFilters",
        "operation": "update"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "eb2c31e8-7bb9-4842-83de-1483f5ffd1b1",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1488,
        16
      ],
      "parameters": {
        "width": 1072,
        "height": 432,
        "content": "# Phase 1\n- Every email gets stripped of HTML\n- Categorized via LLM\n- Filter for spam"
      },
      "typeVersion": 1
    },
    {
      "id": "4c30cd33-812b-43a7-9403-ef5d0a9d35d0",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2560,
        16
      ],
      "parameters": {
        "color": 4,
        "width": 864,
        "height": 704,
        "content": "# Phase 2\n- Conversation history retrieved to provide AI agent context\n- AI agent has access to FAQ and Email Reply Template database stored in Supabase and will make one of two decisions: Flag for human review or reply using answer found in FAQ"
      },
      "typeVersion": 1
    },
    {
      "id": "fc47df97-6055-412d-8f6a-a6979157ba0a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3424,
        16
      ],
      "parameters": {
        "color": 5,
        "width": 864,
        "height": 496,
        "content": "# Phase 3\n- Route determined by AI agent's ability to answer\n- Email + response sent to a subworkflow to ingest into Supabase to be used as training data and retain conversation history"
      },
      "typeVersion": 1
    },
    {
      "id": "2524c1d1-9364-41fd-9cfd-64ec3f7ddbc8",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        992,
        752
      ],
      "parameters": {
        "color": 7,
        "width": 864,
        "height": 576,
        "content": "# Phase 4\n- Subworkflow to ingest email + response into Supabase via vector embedding"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "4o": {
      "ai_languageModel": [
        [
          {
            "node": "Email Manager",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "JSON": {
      "ai_outputParser": [
        [
          {
            "node": "Email Manager",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Reply": {
      "main": [
        [
          {
            "node": "Execute Workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FAQ DB": {
      "ai_tool": [
        [
          {
            "node": "Email Manager",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Format": {
      "main": [
        [
          {
            "node": "Email Manager",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reply1": {
      "main": [
        [
          {
            "node": "Outlook Forward",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Forward?": {
      "main": [
        [
          {
            "node": "Reply1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate1": {
      "main": [
        [
          {
            "node": "Categorize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Categorize": {
      "main": [
        [
          {
            "node": "Spam Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean HTML": {
      "main": [
        [
          {
            "node": "Retrieve Categories",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Spam Filter": {
      "main": [
        [
          {
            "node": "Conversation Retrieval",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Items1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Manager": {
      "main": [
        [
          {
            "node": "Forward?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Outlook Forward": {
      "main": [
        [
          {
            "node": "Execute Workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute Workflow": {
      "main": [
        [
          {
            "node": "Loop Over Items1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items1": {
      "main": [
        [],
        [
          {
            "node": "Clean HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Template DB": {
      "ai_tool": [
        [
          {
            "node": "Email Manager",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings OpenAI1": {
      "ai_embedding": [
        [
          {
            "node": "Email Template DB",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings OpenAI2": {
      "ai_embedding": [
        [
          {
            "node": "FAQ DB",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings OpenAI6": {
      "ai_embedding": [
        [
          {
            "node": "Supabase Vector Store4",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Categories": {
      "main": [
        [
          {
            "node": "Aggregate1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Default Data Loader3": {
      "ai_document": [
        [
          {
            "node": "Supabase Vector Store4",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "Conversation Retrieval": {
      "main": [
        [
          {
            "node": "Format",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase Vector Store4": {
      "main": [
        [
          {
            "node": "Update a row2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Microsoft Outlook Trigger": {
      "main": [
        [
          {
            "node": "Loop Over Items1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Executed by Another Workflow": {
      "main": [
        [
          {
            "node": "Supabase Vector Store4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Recursive Character Text Splitter3": {
      "ai_textSplitter": [
        [
          {
            "node": "Default Data Loader3",
            "type": "ai_textSplitter",
            "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.

Pro

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

About this workflow

Ideal for businesses that receive frequent inquiries about products or services and want to automate responses, freeing up time to focus on core operations. Polls your inbox for new incoming emails Cleans HTML to reduce token usage and filters spam Categorizes emails for better…

Source: https://n8n.io/workflows/9970/ — 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

A lightweight, self-hosted AI assistant built entirely in n8n. Multi-channel messaging (Telegram, WhatsApp, Gmail), persistent memory, task management, and autonomous work — all in a single visual wor

Telegram Trigger, OpenRouter Chat, Data Table +20
AI & RAG

Your AI workforce is ready. Are you?

Google Sheets Tool, Mcp Trigger, Google Drive +29
AI & RAG

This intelligent chatbot leverages cutting-edge financial APIs and AI-driven analysis to deliver comprehensive stock research reports. Get instant access to professional-grade investment analysis that

Tool Think, Supabase Vector Store, OpenAI Embeddings +15
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