{
  "name": "AI Support Responder (EmailConnect + Notion KB)",
  "nodes": [
    {
      "parameters": {
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        -1248,
        272
      ],
      "id": "1dc4abfe-44c6-4c4e-8db3-b63cddbeca38",
      "name": "OpenRouter Chat Model",
      "credentials": {
        "openRouterApi": {
          "id": "",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "resource": "databasePage",
        "operation": "getAll",
        "databaseId": {
          "__rl": true,
          "value": "YOUR_NOTION_DATABASE_ID",
          "mode": "id",
          "__regex": "^([0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12})"
        },
        "returnAll": true,
        "options": {}
      },
      "id": "9b2aa52e-c832-4bcb-9310-a7104ffef04b",
      "name": "Get All KB Questions",
      "type": "n8n-nodes-base.notion",
      "position": [
        -1632,
        32
      ],
      "typeVersion": 2,
      "credentials": {
        "notionApi": {
          "id": "",
          "name": "Notion account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const raw = $input.first().json.output?.toString().trim() ?? '';\nconst matchId = /^NO_MATCH$/i.test(raw) ? 'NO_MATCH' : (raw.match(/\\d+/) || ['NO_MATCH'])[0];\nconst kbData = $('Format KB data').first().json.kbData\nconst userEmail = $('Format KB data').first().json.userEmail\nconst userQuestion = $('Format KB data').first().json.userQuestion\n\nif (matchId === \"NO_MATCH\") {\n  return [{\n    json: {\n      found: false,\n      escalate: true,\n      message: \"ESCALATE_TO_HUMAN\"\n    }\n  }];\n}\n\nconst matchedEntry = kbData[parseInt(matchId)];\nif (!matchedEntry) {\n  return [{\n    json: {\n      found: false,\n      escalate: true, \n      message: \"ESCALATE_TO_HUMAN\"\n    }\n  }];\n}\n\nreturn [{\n  json: {\n    found: true,\n    escalate: false,\n    matchedQuestion: matchedEntry.question,\n    matchedAnswer: matchedEntry.answer,\n    matchedHelpUrl: matchedEntry.help_article,\n    userEmail: userEmail,\n    userQuestion: userQuestion,\n    confidence: \"high\"\n  }\n}];"
      },
      "id": "a94448c6-a186-480d-9796-ff88a6427778",
      "name": "Get Answer",
      "type": "n8n-nodes-base.code",
      "position": [
        -832,
        32
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=You are an expert at finding semantic similarity between questions. \n\n# Your task\nYou will be given a user's question and a list of questions from our knowledge base. Your job is to find the question that is most semantically similar to what the user is asking.\n\nUSER QUESTION: {{ $json.userQuestion }}\n\nAVAILABLE KB QUESTIONS:\n{{ $json.questionsText }}\n\nAnalyze the user's question and find the most relevant KB entry. Consider:\n- Exact keyword matches\n- Semantic similarity \n- Intent similarity\n- Context clues\n\nRespond with ONLY the ID number of the best matching question. If no good match exists (confidence < 70%), respond with \"NO_MATCH\".\n\nBest match ID:",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        -1184,
        32
      ],
      "id": "bf75911d-b8c2-4461-9410-42ea5f0c6703",
      "name": "Find Best Match"
    },
    {
      "parameters": {
        "jsCode": "// Extract all Q&A pairs from Notion KB\nconst kbData = $('Get All KB Questions').all().map((item, \nindex) => {\n  return {\n    id: index,\n    question: item.json.property_question || 'No question',\n    answer: item.json.property_answer || 'No answer',\n    help_article: item.json.property_help_url || ''\n  };\n});\n\n// Format for similarity comparison - include both question and context\nconst questionsForComparison = kbData.map(item =>\n  `ID:${item.id} | Q: ${item.question} | Context: ${item.answer.substring(0, 120)}`\n).join('\\n');\n\n// Robust email data extraction - handles both wrapped and unwrapped payloads\nconst emailData = $('Webhook').first().json;\nconst message = emailData.body?.message ||\nemailData.message;\n\nreturn [{\n  json: {\n    kbData: kbData,\n    questionsText: questionsForComparison,\n    userName: message.sender.name,\n    userEmail: message.sender.email,\n    userQuestion: message.content.text,\n    totalQuestions: kbData.length\n  }\n}];"
      },
      "id": "1bc6d889-f0b4-4892-a8fa-f2aac7f2333d",
      "name": "Format KB data",
      "type": "n8n-nodes-base.code",
      "position": [
        -1408,
        32
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "3551762c-fad0-4305-838f-6e1c54a59d98",
              "leftValue": "={{ $json.escalate }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -608,
        32
      ],
      "id": "f1d7032e-a7e7-4c5c-82a9-1bd619da52af",
      "name": "If"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        -384,
        656
      ],
      "id": "d917ebde-f7c3-4334-a4a2-f5643e51162b",
      "name": "OpenRouter Chat Model1",
      "credentials": {
        "openRouterApi": {
          "id": "",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "jsonSchemaExample": "{\n\t\"subject\": \"Email subject\",\n\t\"message\": \"Hi there, thanks for reaching out!\"\n}"
      },
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        -128,
        656
      ],
      "id": "4fcfbf7a-fdd1-490b-a92f-1cb45dbfdcbb",
      "name": "Structured Output Parser"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        -384,
        144
      ],
      "id": "e5632eee-b768-4436-906a-461ba84c796b",
      "name": "OpenRouter Chat Model2",
      "credentials": {
        "openRouterApi": {
          "id": "",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "jsonSchemaExample": "{\n\t\"subject\": \"Email subject\",\n\t\"message\": \"Hi there, thanks for reaching out!\"\n}"
      },
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        -128,
        144
      ],
      "id": "93b56424-e2b2-440f-bf8a-f44594c463ee",
      "name": "Structured Output Parser1"
    },
    {
      "parameters": {
        "fromEmail": "Support <support@yourdomain.com>",
        "toEmail": "you@yourdomain.com",
        "subject": "[!] Escalated question",
        "emailFormat": "text",
        "text": "=A question was raised, currently not in the KB. Please\n  review, and answer the user directly.\n\n  Details as per below.\n\n  From: {{ $('Format KB data').item.json.userName }}\n  Email: {{ $('Format KB data').item.json.userEmail }}\n  Message: {{ $('Format KB data').item.json.userQuestion }}",
        "options": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        -272,
        -272
      ],
      "id": "8d472996-e643-4021-a4a6-44558930b990",
      "name": "Send escalation",
      "credentials": {
        "smtp": {
          "id": "",
          "name": "SMTP account"
        }
      }
    },
    {
      "parameters": {
        "fromEmail": "Support <support@yourdomain.com>",
        "toEmail": "={{ $('Format KB data').item.json.userName }} <{{ $('Format KB data').item.json.userEmail }}>",
        "subject": "={{ $json.output.subject }}",
        "emailFormat": "text",
        "text": "={{ $json.output.message }}",
        "options": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        80,
        -80
      ],
      "id": "b4e1ba9e-65f6-4248-8322-bd4d64684c33",
      "name": "Inform escalation email",
      "credentials": {
        "smtp": {
          "id": "",
          "name": "SMTP account"
        }
      }
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=We've received a support request email. You are a supportive, friendly customer success manager, tasked with responding to their question.\n\n# Details\n## Sender\nName: {{ $('Format KB data').item.json.userName }}\nEmail: {{ $('Format KB data').item.json.userEmail }}\n\n## Original e-mail message\n{{ $('Format KB data').item.json.userQuestion }}\n\n## Result\nAs we don't have a direct answer for sender, we've escalated the question for further follow up.\n\n# Task\nDraft an e-mail to the sender that informs them that their questions has been escalated and should soon be answered.\n\n# Guidelines\nReview the user's original e-mail message and make sure you:\n- Check for recent conversations from the same sender ({{ $('Format KB data').item.json.userEmail }}) in the attached memory tool to refer to if relevant.\n- Meet their tone, e.g. formal vs informal \n- Meet their depth, e.g. to the point vs detail\n- Be professional and concise, yet warm",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "- Write in normal English sentence case. Do not capitalize every noun.\n- Close your message as a customer success agent"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        -336,
        -80
      ],
      "id": "bd9e826e-9ab9-4260-9d97-08cabcbd8419",
      "name": "Draft escalation"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=We've received a support request email. You are a supportive, friendly customer success manager, tasked with responding to their question.\n\n# Details\n## Sender\nName: {{ $('Format KB data').item.json.userName }}\nEmail: {{ $('Format KB data').item.json.userEmail }}\n\n## Original e-mail message\n{{ $json.userQuestion }}\n\n## Matched answer in our KB\n{{ $('Get Answer').item.json.matchedAnswer }}\n\n## Further reading (include in reply only if relevant and present)\n{{ $('Get Answer').item.json.matchedHelpUrl }}\n\n# Task\nDraft an e-mail to the sender that incorporates the answer found in the KB to address their question. \n\n# Guidelines\nReview the user's original e-mail message for their question and make sure you:\n- Tailor the KBs answer to their specific question and remove fluf. Be to the point. \n- Check recent conversations from the same sender ({{ $('Format KB data').item.json.userEmail }}) in the attached memory tool to refer to if relevant.\n- Meet their tone, e.g. formal vs informal \n- Meet their depth, e.g. to the point vs detail\n- Be professional and concise, yet warm",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "- Write in normal English sentence case. Do not capitalize every noun.\n- Close your message as a customer success agent"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        -336,
        432
      ],
      "id": "a132971f-f071-45f7-ab61-9f2885b43fe0",
      "name": "Draft answer"
    },
    {
      "parameters": {
        "fromEmail": "Support <support@yourdomain.com>",
        "toEmail": "={{ $('Format KB data').item.json.userName }} <{{ $('Format KB data').item.json.userEmail }}>",
        "subject": "={{ $json.output.subject }}",
        "emailFormat": "text",
        "text": "={{ $json.output.message }}",
        "options": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        80,
        432
      ],
      "id": "c53a0112-896d-4fcf-964e-ea8597a8f640",
      "name": "Send answer email",
      "credentials": {
        "smtp": {
          "id": "",
          "name": "SMTP account"
        }
      }
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Format KB data').item.json.userEmail }}",
        "contextWindowLength": 8
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        -256,
        656
      ],
      "id": "3815ea82-cc09-480e-8e51-2318b0910b69",
      "name": "Simple Memory"
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Format KB data').item.json.userEmail }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        -256,
        144
      ],
      "id": "0a7f3a74-ab4a-410c-ad5e-9c8dc259be25",
      "name": "Simple Memory1"
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Format KB data').item.json.userEmail }}",
        "contextWindowLength": 8
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        -1088,
        272
      ],
      "id": "f4c7d0f9-c77e-48aa-9c75-787a3147a394",
      "name": "Simple Memory2"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "your-webhook-path/emailconnect",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1840,
        32
      ],
      "id": "5f15601a-b837-4cc6-ad1c-1d91bc3c42b8",
      "name": "Webhook",
      "webhookId": "your-webhook-id"
    }
  ],
  "pinData": {
    "Webhook": [
      {
        "json": {
          "headers": {
            "host": "your-n8n-instance.com",
            "user-agent": "EmailConnect/1.0",
            "content-type": "application/json",
            "x-email-webhook": "true"
          },
          "params": {},
          "query": {},
          "body": {
            "message": {
              "sender": {
                "name": "Jane Smith",
                "email": "jane@example.com"
              },
              "recipient": {
                "name": null,
                "email": "support@yourdomain.com"
              },
              "subject": "Data security request",
              "content": {
                "text": "Hi there!\n\nCan I use my own S3 provider instead of the one you provide?\nIt's a requirement from our IT department.\n\nThanks!\n"
              },
              "date": "2025-12-24T09:26:49.000Z",
              "attachments": []
            },
            "domainId": "your-domain-id",
            "aliasId": "your-alias-id"
          },
          "webhookUrl": "https://your-n8n-instance.com/webhook/your-webhook-path/emailconnect",
          "executionMode": "production"
        },
        "pairedItem": {
          "item": 0
        }
      }
    ]
  },
  "connections": {
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Find Best Match",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get All KB Questions": {
      "main": [
        [
          {
            "node": "Format KB data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Answer": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Best Match": {
      "main": [
        [
          {
            "node": "Get Answer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format KB data": {
      "main": [
        [
          {
            "node": "Find Best Match",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "Send escalation",
            "type": "main",
            "index": 0
          },
          {
            "node": "Draft escalation",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Draft answer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Draft answer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Draft answer",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "Draft escalation",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser1": {
      "ai_outputParser": [
        [
          {
            "node": "Draft escalation",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Draft escalation": {
      "main": [
        [
          {
            "node": "Inform escalation email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Draft answer": {
      "main": [
        [
          {
            "node": "Send answer email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "Draft answer",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory1": {
      "ai_memory": [
        [
          {
            "node": "Draft escalation",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory2": {
      "ai_memory": [
        [
          {
            "node": "Find Best Match",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Get All KB Questions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
