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 →
{
"nodes": [
{
"parameters": {
"jsCode": "const webhookData = $input.first().json;\nconst messages = webhookData.body?.messages || [];\nconst lastUserMsg = messages.filter(m => m.role === 'user').pop()?.content || '';\nconst text = lastUserMsg.toLowerCase();\n\n// Heur\u00edstica para decidir si buscar en la base de conocimientos\nconst isShort = text.length < 60;\nconst hasQuestion = text.includes('?');\nconst isJustEmail = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/.test(text) && text.length < 80;\n\nlet requires_rag = true;\nif ((isShort && !hasQuestion && isJustEmail) || ['hola', 'hi', 'hello', 'buenos d\u00edas', 'gracias', 'thanks'].includes(text.trim())) {\n requires_rag = false;\n}\n\nreturn [{\n json: {\n ...webhookData,\n requires_rag\n }\n}];"
},
"id": "[NODE_ID_1]",
"name": "Analyze Intent",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-2272,
1104
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"id": "cond1",
"leftValue": "={{ $json.requires_rag }}",
"rightValue": "={{ true }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "[NODE_ID_2]",
"name": "Needs Knowledge Base?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-2048,
1104
]
},
{
"parameters": {
"httpMethod": "POST",
"path": "mga-ai-agent",
"responseMode": "responseNode",
"options": {
"allowedOrigins": "https://www.mgatc.com,https://mgatc.pages.dev,http://localhost:3000,http://localhost:3001,https://mgatc.com"
}
},
"id": "[NODE_ID_3]",
"name": "Webhook Trigger1",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-2496,
1104
]
},
{
"parameters": {
"jsCode": "const response = $input.first().json;\nconst error = response.error;\nconst assistantMessage = error ? '' : (response.choices?.[0]?.message?.content || '');\n\nconst webhookBody = $('Webhook Trigger1').first().json.body;\nconst allMessages = webhookBody.messages || []; // Aseguramos que sea un array\nconst conversationStr = JSON.stringify(allMessages);\n\n// PRIORIDAD: Datos directos del nuevo formulario de identidad\nlet email = webhookBody.user_info?.email || null;\nlet name = webhookBody.user_info?.name || null;\n\n// FALLBACK: Regex\nif (!email) {\n const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g;\n const emails = conversationStr.match(emailRegex) || [];\n email = emails[0] || null;\n}\n\nif (!name) {\n const nameRegex = /(?:me llamo|soy|my name is|i am|i\\'m)\\s+([A-Z][a-z\u00e1\u00e9\u00ed\u00f3\u00fa\u00c1\u00c9\u00cd\u00d3\u00daa-z]+(?:\\s+[A-Z][a-z\u00e1\u00e9\u00ed\u00f3\u00fa\u00c1\u00c9\u00cd\u00d3\u00daa-z]+)?)/gi;\n const nameMatch = nameRegex.exec(conversationStr);\n name = nameMatch ? nameMatch[1] : null;\n}\n\nconst systemMsg = allMessages.find(m => m.role === 'system');\nconst sourceLang = systemMsg?.content?.includes('currently set to English') ? 'en' : 'es';\n\n// --- GENERACI\u00d3N DE HTML SEGURO ---\nlet chatHtml = \"\";\n\ntry {\n // Tomamos los \u00faltimos mensajes y quitamos el de sistema\n const chatMessages = allMessages.filter(m => m.role !== 'system').slice(-6);\n \n if (chatMessages.length > 0) {\n chatHtml = chatMessages.map(msg => {\n const isUser = msg.role === 'user';\n const label = isUser ? 'Usuario' : 'Asistente';\n const bgColor = isUser ? '#f8fafc' : '#ffffff';\n const borderColor = isUser ? '#2563eb' : '#10b981';\n \n return `\n <div style=\"margin-bottom: 12px; padding: 12px; border-radius: 8px; background-color: ${bgColor}; border-left: 4px solid ${borderColor}; font-family: sans-serif;\">\n <strong style=\"display: block; font-size: 11px; color: #64748b; text-transform: uppercase; margin-bottom: 4px;\">${label}</strong>\n <div style=\"font-size: 14px; color: #1e293b; line-height: 1.5;\">${msg.content}</div>\n </div>`;\n }).join('');\n } else {\n chatHtml = \"<p>No hay mensajes recientes en la conversaci\u00f3n.</p>\";\n }\n} catch (e) {\n chatHtml = \"<p>Error al formatear la conversaci\u00f3n.</p>\";\n}\n\nreturn [{\n json: {\n error,\n assistantMessage,\n email,\n name,\n sourceLang,\n hasLead: !!(email || name),\n chatHtml, // Ahora s\u00ed o s\u00ed estar\u00e1 presente\n conversation: allMessages.slice(-6) // Lo mantenemos por si lo necesitas\n }\n}];"
},
"id": "[NODE_ID_4]",
"name": "Extract Lead Data1",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1152,
1104
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { content: $json.assistantMessage || ($json.error ? 'Error: ' + $json.error.message : 'No se pudo obtener respuesta de la IA.') } }}",
"options": {}
},
"id": "[NODE_ID_5]",
"name": "Respond to Frontend2",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
-928,
1008
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"id": "e5fda4e4-0e70-4b06-a9d7-5664090c13ea",
"leftValue": "={{ $json.hasLead }}",
"rightValue": "={{ true }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "[NODE_ID_6]",
"name": "Has Lead Info?1",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-928,
1200
]
},
{
"parameters": {
"tableId": "leads",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "name",
"fieldValue": "={{ $json.name || null }}"
},
{
"fieldId": "email",
"fieldValue": "={{ $json.email || null }}"
},
{
"fieldId": "interest",
"fieldValue": "={{ $json.assistantMessage.substring(0, 300) }}"
},
{
"fieldId": "source_lang",
"fieldValue": "={{ $json.sourceLang }}"
},
{
"fieldId": "source_page",
"fieldValue": "={{ $('Webhook Trigger1').first().json.body.source_page || 'unknown' }}"
},
{
"fieldId": "conversation",
"fieldValue": "={{ JSON.stringify($json.conversation) }}"
},
{
"fieldId": "status",
"fieldValue": "new"
}
]
}
},
"id": "[NODE_ID_7]",
"name": "Save Lead to Supabase1",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
-704,
1104
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"html": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; margin: 0; padding: 0; background-color: #f4f4f4; }\n .container { max-width: 600px; margin: 20px auto; background: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }\n .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 40px 20px; text-align: center; }\n .logo { max-width: 200px; height: auto; margin-bottom: 20px; }\n .header h1 { color: #ffffff; margin: 0; font-size: 24px; }\n .content { padding: 40px 30px; }\n .content h2 { color: #667eea; margin-top: 0; font-size: 20px; }\n .content p { margin: 15px 0; }\n .highlight { background-color: #f8f9ff; padding: 20px; border-left: 4px solid #667eea; margin: 20px 0; }\n .chat-box { background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 4px; padding: 15px; margin-top: 15px; font-size: 14px; color: #555; white-space: pre-line; }\n .footer { background-color: #f8f9fa; padding: 20px; text-align: center; font-size: 12px; color: #666; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <img src=\"https://raw.githubusercontent.com/Mgobeaalcoba/Mgobeaalcoba.github.io/6e7c26232117f9e2d85828b6477192e1e89f34b4/assets/images/logo_claro.png\" alt=\"MGA Tech\" class=\"logo\">\n <h1>\u00a1Nuevo Lead Detectado!</h1>\n </div>\n <div class=\"content\">\n <h2>Alerta del Agente IA</h2>\n <p>Se ha detectado un nuevo lead interactuando con el chat del Agente IA. A continuaci\u00f3n tienes los detalles:</p>\n \n <div class=\"highlight\">\n <p style=\"margin: 5px 0;\"><strong>Nombre:</strong> {{ $json.name || 'No detectado' }}</p>\n <p style=\"margin: 5px 0;\"><strong>Email:</strong> {{ $json.email || 'No detectado' }}</p>\n <p style=\"margin: 5px 0;\"><strong>Idioma:</strong> {{ $json.sourceLang === 'en' ? 'Ingl\u00e9s' : 'Espa\u00f1ol' }}</p>\n </div>\n\n <h2 style=\"margin-top: 30px; font-size: 18px;\">\u00daltimos mensajes de la conversaci\u00f3n:</h2>\n <div class=\"chat-box\">\n {{ $('Extract Lead Data1').item.json.chatHtml }}\n </div>\n \n </div>\n <div class=\"footer\">\n <p>Esta es una notificaci\u00f3n autom\u00e1tica interna del sistema.</p>\n <p>© 2026 MGA Tech Consulting. Todos los derechos reservados.</p>\n </div>\n </div>\n</body>\n</html>"
},
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [
-480,
1104
],
"id": "[NODE_ID_8]",
"name": "HTML1"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
-704,
1296
],
"id": "[NODE_ID_9]",
"name": "No Operation, do nothing1"
},
{
"parameters": {
"method": "POST",
"url": "https://router.huggingface.co/v1/chat/completions",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ \n JSON.stringify({ \n messages: (function() { \n const webhookData = $('Webhook Trigger1').first().json; \n const history = (webhookData.body && webhookData.body.messages) ? webhookData.body.messages : []; \n const knowledgeData = $('Search Knowledge Supabase').first().json; \n const contextChunks = Array.isArray(knowledgeData) ? knowledgeData.map(r => r.content).join('\\n---\\n') : (knowledgeData.content || ''); \n const systemMsg = history.find(m => m.role === 'system'); \n const otherMsgs = history.filter(m => m.role !== 'system'); \n return [ \n { \n role: 'system', \n content: (systemMsg?.content || 'You are a helpful assistant.') + '\\n\\nKNOWLEDGE BASE CONTEXT (Usa esto para responder solo si es relevante):\\n' + contextChunks \n }, \n ...otherMsgs \n ]; \n })(), \n model: 'zai-org/GLM-4.7-Flash:novita', \n stream: false \n }) \n}}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
-1376,
1008
],
"id": "[NODE_ID_10]",
"name": "Call Hugging Face RAG",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://router.huggingface.co/ v1/chat/completions",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "=={{ \n JSON.stringify({ \n messages: $('Webhook Trigger1').first().json.body.messages, \n model: 'zai-org/GLM-4.7-Flash:novita', \n stream: false \n }) \n}}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
-1376,
1200
],
"id": "[NODE_ID_11]",
"name": "Call Hugging Face General",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://router.huggingface.co/hf-inference/models/intfloat/multilingual-e5-small/pipeline/feature-extraction",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "inputs",
"value": "={{ $json.body.messages[0].content }}"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": true
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
-1824,
1008
],
"id": "[NODE_ID_12]",
"name": "Hugging Face Embedding Model",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $vars.NEXT_PUBLIC_SUPABASE_URL }}/rest/v1/rpc/match_knowledge",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $vars.NEXT_PUBLIC_SUPABASE_ANON_KEY }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $vars.NEXT_PUBLIC_SUPABASE_ANON_KEY }}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"query_embedding\": {{ JSON.stringify($json.body) }},\n \"match_threshold\": 0.5,\n \"match_count\": 3\n}",
"options": {}
},
"id": "[NODE_ID_13]",
"name": "Search Knowledge Supabase",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
-1600,
1008
]
},
{
"parameters": {
"fromEmail": "mariano@mgatc.com",
"toEmail": "gobeamariano@gmail.com",
"subject": "Nuevo Lead Detectado",
"html": "={{ $json.html }}",
"options": {}
},
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [
-256,
1104
],
"id": "[NODE_ID_14]",
"name": "Send an Email",
"credentials": {
"smtp": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Analyze Intent": {
"main": [
[
{
"node": "Needs Knowledge Base?",
"type": "main",
"index": 0
}
]
]
},
"Needs Knowledge Base?": {
"main": [
[
{
"node": "Hugging Face Embedding Model",
"type": "main",
"index": 0
}
],
[
{
"node": "Call Hugging Face General",
"type": "main",
"index": 0
}
]
]
},
"Webhook Trigger1": {
"main": [
[
{
"node": "Analyze Intent",
"type": "main",
"index": 0
}
]
]
},
"Extract Lead Data1": {
"main": [
[
{
"node": "Respond to Frontend2",
"type": "main",
"index": 0
},
{
"node": "Has Lead Info?1",
"type": "main",
"index": 0
}
]
]
},
"Has Lead Info?1": {
"main": [
[
{
"node": "Save Lead to Supabase1",
"type": "main",
"index": 0
}
],
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
},
"Save Lead to Supabase1": {
"main": [
[
{
"node": "HTML1",
"type": "main",
"index": 0
}
]
]
},
"HTML1": {
"main": [
[
{
"node": "Send an Email",
"type": "main",
"index": 0
}
]
]
},
"Call Hugging Face RAG": {
"main": [
[
{
"node": "Extract Lead Data1",
"type": "main",
"index": 0
}
]
]
},
"Call Hugging Face General": {
"main": [
[
{
"node": "Extract Lead Data1",
"type": "main",
"index": 0
}
]
]
},
"Hugging Face Embedding Model": {
"main": [
[
{
"node": "Search Knowledge Supabase",
"type": "main",
"index": 0
}
]
]
},
"Search Knowledge Supabase": {
"main": [
[
{
"node": "Call Hugging Face RAG",
"type": "main",
"index": 0
}
]
]
}
},
"meta": {
"templateCredsSetupCompleted": true
}
}
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.
httpHeaderAuthsmtpsupabaseApi
About this workflow
Ai Assistant Workflow. Uses supabase, httpRequest, emailSend. Webhook trigger; 14 nodes.
Source: https://github.com/Mgobeaalcoba/Mgobeaalcoba.github.io/blob/main/docs/automations/ai_assistant_workflow.json — original creator credit. Request a take-down →