{ "updatedAt": "2026-06-09T19:06:33.510Z", "createdAt": "2026-06-09T18:15:50.646Z", "id": "1ykpTM58gz0iOxvK", "name": "Sofía MVP - Gmail a Neo4j", "description": null, "active": true, "isArchived": false, "nodes": [ { "parameters": { "pollTimes": { "item": [ { "mode": "everyMinute" } ] }, "filters": { "q": "subject:\"[MVP SOFIA]\"", "sender": "iaracena@gomezleemarketing.com" } }, "type": "n8n-nodes-base.gmailTrigger", "typeVersion": 1.4, "position": [ -112, -80 ], "id": "bd0f5bc5-a7b2-461f-b316-ce76e907ae36", "name": "Gmail Trigger", "credentials": { "gmailOAuth2": { "id": "UDcO1FLJqA453V2D", "name": "Gmail account 3" } } }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "23cd3dcc-62ad-434d-accf-5e9d2ea7c558", "leftValue": "={{ $json.subject }}", "rightValue": "[MVP SOFIA]", "operator": { "type": "string", "operation": "contains" } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 368, -80 ], "id": "83330da0-3df5-4882-8432-3cde6e9c091b", "name": "IF - Es correo MVP Sofía" }, { "parameters": { "jsCode": "const item = $input.first().json;\n\nconst subject =\n item.subject ||\n item.Subject ||\n item.headers?.subject ||\n '';\n\nconst from =\n item.from ||\n item.From ||\n item.headers?.from ||\n '';\n\nconst to =\n item.to ||\n item.To ||\n item.headers?.to ||\n '';\n\nconst text =\n item.textPlain ||\n item.text ||\n item.snippet ||\n item.body ||\n item.message ||\n '';\n\nconst html =\n item.textHtml ||\n item.html ||\n '';\n\nfunction limpiarHtml(valor) {\n return String(valor || '')\n .replace(//gi, ' ')\n .replace(//gi, ' ')\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/&/g, '&')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst body = String(text || '').trim() || limpiarHtml(html);\n\nreturn [\n {\n json: {\n source: 'gmail',\n subject: String(subject).trim(),\n from: String(from).trim(),\n to: String(to).trim(),\n body,\n received_at: new Date().toISOString(),\n raw: item,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 128, -80 ], "id": "15546536-4579-4015-9782-768095190afe", "name": "Code - Normalizar correo MVP Sofía" }, { "parameters": { "method": "POST", "url": "https://proyectosit-flowise.btv4al.easypanel.host/api/v1/prediction/1d68f530-9d5d-4229-9bd6-d5ea698832fc ", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"question\": \"={{ $json.body }}\"\n}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 752, -96 ], "id": "50675419-4b0c-4d2e-878a-69cfb38682f8", "name": "HTTP Request - Flowise extraer caso" }, { "parameters": { "jsCode": "const item = $input.first().json;\n\nlet respuesta =\n item.text ||\n item.answer ||\n item.response ||\n item.result ||\n item.data ||\n item;\n\nif (typeof respuesta !== 'string') {\n respuesta = JSON.stringify(respuesta);\n}\n\nrespuesta = respuesta\n .replace(/```json/gi, '')\n .replace(/```/g, '')\n .trim();\n\nlet data;\n\ntry {\n data = JSON.parse(respuesta);\n} catch (error) {\n throw new Error(\n 'Flowise no devolvió JSON válido. Respuesta recibida: ' + respuesta\n );\n}\n\nconst camposObligatorios = [\n 'responsable_nombre',\n 'responsable_email',\n 'cliente',\n 'titulo',\n 'problema',\n 'solucion',\n 'resultado',\n 'area',\n 'confidence',\n];\n\nfor (const campo of camposObligatorios) {\n if (!(campo in data)) {\n data[campo] = null;\n }\n}\n\nreturn [\n {\n json: {\n ...data,\n fuente: 'gmail_flowise_mvp',\n procesado_en: new Date().toISOString(),\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 960, -96 ], "id": "cd88f8fb-d68b-4990-8ee1-a2bb0c85d9fe", "name": "Code - Parsear JSON Flowise" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "a73cb1f1-a67f-4023-8454-9b9e9dec51b4", "leftValue": "={{ $json.confidence }}", "rightValue": 0.6, "operator": { "type": "number", "operation": "gte" } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 1168, -96 ], "id": "020c0fcd-1aa9-4a64-be98-3cd9ee47f6be", "name": "If" }, { "parameters": { "jsCode": "const data = $input.first().json;\n\nfunction texto(valor, fallback = '') {\n return String(valor ?? fallback).trim();\n}\n\nconst responsableEmail = texto(data.responsable_email || 'desconocido@gomezleemarketing.com').toLowerCase();\nconst responsableNombre = texto(data.responsable_nombre || 'Responsable no identificado');\nconst cliente = texto(data.cliente || 'Cliente no identificado');\nconst titulo = texto(data.titulo || 'Caso sin título');\nconst problema = texto(data.problema || 'Problema no identificado');\nconst solucion = texto(data.solucion || 'Solución no identificada');\nconst resultado = texto(data.resultado || 'Resultado no identificado');\nconst area = texto(data.area || 'Área no identificada');\nconst confidence = Number(data.confidence ?? 0);\nconst fuente = texto(data.fuente || 'gmail_flowise_mvp');\nconst procesadoEn = texto(data.procesado_en || new Date().toISOString());\n\nconst cypher = `\nMERGE (empleado:Empleado {email: $responsableEmail})\nSET empleado.nombre = $responsableNombre,\n empleado.area = $area,\n empleado.actualizado_en = datetime()\n\nMERGE (cliente:Cliente {nombre: $cliente})\nSET cliente.actualizado_en = datetime()\n\nCREATE (caso:CasoResuelto {\n id: randomUUID(),\n titulo: $titulo,\n problema: $problema,\n solucion: $solucion,\n resultado: $resultado,\n area: $area,\n confidence: $confidence,\n fuente: $fuente,\n procesado_en: datetime($procesadoEn),\n fecha_captura: datetime()\n})\n\nMERGE (empleado)-[:RESOLVIO]->(caso)\nMERGE (caso)-[:PERTENECE_A_CLIENTE]->(cliente)\n\nRETURN caso.id AS caso_id,\n caso.titulo AS titulo,\n empleado.nombre AS responsable,\n cliente.nombre AS cliente;\n`;\n\nreturn [\n {\n json: {\n cypher,\n parameters: {\n responsableEmail,\n responsableNombre,\n cliente,\n titulo,\n problema,\n solucion,\n resultado,\n area,\n confidence,\n fuente,\n procesadoEn,\n },\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1648, -128 ], "id": "792b2605-b658-4ac5-8cfd-f0148895bd80", "name": "Code - Preparar Cypher Neo4j" }, { "parameters": { "method": "POST", "url": "http://neo4j:7474/db/neo4j/tx/commit", "authentication": "genericCredentialType", "genericAuthType": "httpBasicAuth", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" }, { "name": "Accept", "value": " application/json" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={{\n {\n statements: [\n {\n statement: $json.cypher,\n parameters: $json.parameters\n }\n ]\n }\n}}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 1856, -128 ], "id": "cc3b3878-dd68-499d-8f1f-f5e287bc5c11", "name": "HTTP Request - Crear caso en Neo4j", "credentials": { "httpBasicAuth": { "id": "nIxZ7elcHvuzsRKW", "name": "Neo4j" } } }, { "parameters": { "content": "## Entrada controlada \n\nEste flujo solo procesa correos de prueba.\n\nGmail recibe el correo, n8n normaliza el asunto/cuerpo y el IF evita procesar correos que no pertenezcan al MVP.\n", "height": 288, "width": 768, "color": 6 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ -192, -208 ], "id": "fe25ffcb-41c7-460e-9ded-c368cfd5ae45", "name": "Sticky Note" }, { "parameters": { "content": "## Extracción inteligente con Flowise\n\nn8n envía el cuerpo del correo a Flowise.\n\nFlowise devuelve un JSON estructurado con responsable, cliente, título, problema, solución, resultado, área y confidence.\n\nSolo los casos con confidence suficiente continúan hacia Neo4j.\n", "height": 384, "width": 688 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 688, -272 ], "id": "8df77283-b1ad-4e19-b9a6-30f8034e7608", "name": "Sticky Note1" }, { "parameters": { "content": "## Registro en Neo4j y confirmación\n\nn8n convierte el JSON en una consulta Cypher y crea el caso en Neo4j.\n\nSe crean/actualizan nodos de Empleado, Cliente y CasoResuelto, con sus relaciones.\n\n", "height": 384, "width": 688, "color": "#0E6808" }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 1456, -288 ], "id": "72c16df2-30a4-441e-9b38-e14e62c43040", "name": "Sticky Note2" } ], "connections": { "Gmail Trigger": { "main": [ [ { "node": "Code - Normalizar correo MVP Sofía", "type": "main", "index": 0 } ] ] }, "IF - Es correo MVP Sofía": { "main": [ [ { "node": "HTTP Request - Flowise extraer caso", "type": "main", "index": 0 } ] ] }, "Code - Normalizar correo MVP Sofía": { "main": [ [ { "node": "IF - Es correo MVP Sofía", "type": "main", "index": 0 } ] ] }, "HTTP Request - Flowise extraer caso": { "main": [ [ { "node": "Code - Parsear JSON Flowise", "type": "main", "index": 0 } ] ] }, "Code - Parsear JSON Flowise": { "main": [ [ { "node": "If", "type": "main", "index": 0 } ] ] }, "If": { "main": [ [ { "node": "Code - Preparar Cypher Neo4j", "type": "main", "index": 0 } ] ] }, "Code - Preparar Cypher Neo4j": { "main": [ [ { "node": "HTTP Request - Crear caso en Neo4j", "type": "main", "index": 0 } ] ] }, "HTTP Request - Crear caso en Neo4j": { "main": [ [] ] } }, "settings": { "executionOrder": "v1", "binaryMode": "separate" }, "staticData": { "node:Gmail Trigger": { "Gmail Trigger": { "lastTimeChecked": 1781031641, "possibleDuplicates": [ "19eadc22a5cb770f" ] } } }, "meta": { "templateCredsSetupCompleted": true }, "versionId": "da03274d-aca0-4d78-be28-6543d5ecad92", "activeVersionId": "da03274d-aca0-4d78-be28-6543d5ecad92", "versionCounter": 124, "triggerCount": 1, "shared": [ { "updatedAt": "2026-06-09T18:15:50.654Z", "createdAt": "2026-06-09T18:15:50.654Z", "role": "workflow:owner", "workflowId": "1ykpTM58gz0iOxvK", "projectId": "PJpTANzTXIFibWsW", "project": { "updatedAt": "2026-04-22T14:25:09.686Z", "createdAt": "2026-04-22T14:22:54.790Z", "id": "PJpTANzTXIFibWsW", "name": "Isaac Aracena ", "type": "personal", "icon": null, "description": null, "creatorId": "0a88c0b1-928e-4412-896e-c5d1c99b2029" } } ], "tags": [], "activeVersion": { "updatedAt": "2026-06-09T19:00:41.000Z", "createdAt": "2026-06-09T18:56:18.699Z", "versionId": "da03274d-aca0-4d78-be28-6543d5ecad92", "workflowId": "1ykpTM58gz0iOxvK", "nodes": [ { "parameters": { "pollTimes": { "item": [ { "mode": "everyMinute" } ] }, "filters": { "q": "subject:\"[MVP SOFIA]\"", "sender": "iaracena@gomezleemarketing.com" } }, "type": "n8n-nodes-base.gmailTrigger", "typeVersion": 1.4, "position": [ -112, -80 ], "id": "bd0f5bc5-a7b2-461f-b316-ce76e907ae36", "name": "Gmail Trigger", "credentials": { "gmailOAuth2": { "id": "UDcO1FLJqA453V2D", "name": "Gmail account 3" } } }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "23cd3dcc-62ad-434d-accf-5e9d2ea7c558", "leftValue": "={{ $json.subject }}", "rightValue": "[MVP SOFIA]", "operator": { "type": "string", "operation": "contains" } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 368, -80 ], "id": "83330da0-3df5-4882-8432-3cde6e9c091b", "name": "IF - Es correo MVP Sofía" }, { "parameters": { "jsCode": "const item = $input.first().json;\n\nconst subject =\n item.subject ||\n item.Subject ||\n item.headers?.subject ||\n '';\n\nconst from =\n item.from ||\n item.From ||\n item.headers?.from ||\n '';\n\nconst to =\n item.to ||\n item.To ||\n item.headers?.to ||\n '';\n\nconst text =\n item.textPlain ||\n item.text ||\n item.snippet ||\n item.body ||\n item.message ||\n '';\n\nconst html =\n item.textHtml ||\n item.html ||\n '';\n\nfunction limpiarHtml(valor) {\n return String(valor || '')\n .replace(//gi, ' ')\n .replace(//gi, ' ')\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/&/g, '&')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst body = String(text || '').trim() || limpiarHtml(html);\n\nreturn [\n {\n json: {\n source: 'gmail',\n subject: String(subject).trim(),\n from: String(from).trim(),\n to: String(to).trim(),\n body,\n received_at: new Date().toISOString(),\n raw: item,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 128, -80 ], "id": "15546536-4579-4015-9782-768095190afe", "name": "Code - Normalizar correo MVP Sofía" }, { "parameters": { "method": "POST", "url": "https://proyectosit-flowise.btv4al.easypanel.host/api/v1/prediction/1d68f530-9d5d-4229-9bd6-d5ea698832fc ", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"question\": \"={{ $json.body }}\"\n}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 752, -96 ], "id": "50675419-4b0c-4d2e-878a-69cfb38682f8", "name": "HTTP Request - Flowise extraer caso" }, { "parameters": { "jsCode": "const item = $input.first().json;\n\nlet respuesta =\n item.text ||\n item.answer ||\n item.response ||\n item.result ||\n item.data ||\n item;\n\nif (typeof respuesta !== 'string') {\n respuesta = JSON.stringify(respuesta);\n}\n\nrespuesta = respuesta\n .replace(/```json/gi, '')\n .replace(/```/g, '')\n .trim();\n\nlet data;\n\ntry {\n data = JSON.parse(respuesta);\n} catch (error) {\n throw new Error(\n 'Flowise no devolvió JSON válido. Respuesta recibida: ' + respuesta\n );\n}\n\nconst camposObligatorios = [\n 'responsable_nombre',\n 'responsable_email',\n 'cliente',\n 'titulo',\n 'problema',\n 'solucion',\n 'resultado',\n 'area',\n 'confidence',\n];\n\nfor (const campo of camposObligatorios) {\n if (!(campo in data)) {\n data[campo] = null;\n }\n}\n\nreturn [\n {\n json: {\n ...data,\n fuente: 'gmail_flowise_mvp',\n procesado_en: new Date().toISOString(),\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 960, -96 ], "id": "cd88f8fb-d68b-4990-8ee1-a2bb0c85d9fe", "name": "Code - Parsear JSON Flowise" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "a73cb1f1-a67f-4023-8454-9b9e9dec51b4", "leftValue": "={{ $json.confidence }}", "rightValue": 0.6, "operator": { "type": "number", "operation": "gte" } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 1168, -96 ], "id": "020c0fcd-1aa9-4a64-be98-3cd9ee47f6be", "name": "If" }, { "parameters": { "jsCode": "const data = $input.first().json;\n\nfunction texto(valor, fallback = '') {\n return String(valor ?? fallback).trim();\n}\n\nconst responsableEmail = texto(data.responsable_email || 'desconocido@gomezleemarketing.com').toLowerCase();\nconst responsableNombre = texto(data.responsable_nombre || 'Responsable no identificado');\nconst cliente = texto(data.cliente || 'Cliente no identificado');\nconst titulo = texto(data.titulo || 'Caso sin título');\nconst problema = texto(data.problema || 'Problema no identificado');\nconst solucion = texto(data.solucion || 'Solución no identificada');\nconst resultado = texto(data.resultado || 'Resultado no identificado');\nconst area = texto(data.area || 'Área no identificada');\nconst confidence = Number(data.confidence ?? 0);\nconst fuente = texto(data.fuente || 'gmail_flowise_mvp');\nconst procesadoEn = texto(data.procesado_en || new Date().toISOString());\n\nconst cypher = `\nMERGE (empleado:Empleado {email: $responsableEmail})\nSET empleado.nombre = $responsableNombre,\n empleado.area = $area,\n empleado.actualizado_en = datetime()\n\nMERGE (cliente:Cliente {nombre: $cliente})\nSET cliente.actualizado_en = datetime()\n\nCREATE (caso:CasoResuelto {\n id: randomUUID(),\n titulo: $titulo,\n problema: $problema,\n solucion: $solucion,\n resultado: $resultado,\n area: $area,\n confidence: $confidence,\n fuente: $fuente,\n procesado_en: datetime($procesadoEn),\n fecha_captura: datetime()\n})\n\nMERGE (empleado)-[:RESOLVIO]->(caso)\nMERGE (caso)-[:PERTENECE_A_CLIENTE]->(cliente)\n\nRETURN caso.id AS caso_id,\n caso.titulo AS titulo,\n empleado.nombre AS responsable,\n cliente.nombre AS cliente;\n`;\n\nreturn [\n {\n json: {\n cypher,\n parameters: {\n responsableEmail,\n responsableNombre,\n cliente,\n titulo,\n problema,\n solucion,\n resultado,\n area,\n confidence,\n fuente,\n procesadoEn,\n },\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1648, -128 ], "id": "792b2605-b658-4ac5-8cfd-f0148895bd80", "name": "Code - Preparar Cypher Neo4j" }, { "parameters": { "method": "POST", "url": "http://neo4j:7474/db/neo4j/tx/commit", "authentication": "genericCredentialType", "genericAuthType": "httpBasicAuth", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" }, { "name": "Accept", "value": " application/json" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={{\n {\n statements: [\n {\n statement: $json.cypher,\n parameters: $json.parameters\n }\n ]\n }\n}}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 1856, -128 ], "id": "cc3b3878-dd68-499d-8f1f-f5e287bc5c11", "name": "HTTP Request - Crear caso en Neo4j", "credentials": { "httpBasicAuth": { "id": "nIxZ7elcHvuzsRKW", "name": "Neo4j" } } }, { "parameters": { "content": "## Entrada controlada \n\nEste flujo solo procesa correos de prueba.\n\nGmail recibe el correo, n8n normaliza el asunto/cuerpo y el IF evita procesar correos que no pertenezcan al MVP.\n", "height": 288, "width": 768, "color": 6 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ -192, -208 ], "id": "fe25ffcb-41c7-460e-9ded-c368cfd5ae45", "name": "Sticky Note" }, { "parameters": { "content": "## Extracción inteligente con Flowise\n\nn8n envía el cuerpo del correo a Flowise.\n\nFlowise devuelve un JSON estructurado con responsable, cliente, título, problema, solución, resultado, área y confidence.\n\nSolo los casos con confidence suficiente continúan hacia Neo4j.\n", "height": 384, "width": 688 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 688, -272 ], "id": "8df77283-b1ad-4e19-b9a6-30f8034e7608", "name": "Sticky Note1" }, { "parameters": { "content": "## Registro en Neo4j y confirmación\n\nn8n convierte el JSON en una consulta Cypher y crea el caso en Neo4j.\n\nSe crean/actualizan nodos de Empleado, Cliente y CasoResuelto, con sus relaciones.\n\n", "height": 384, "width": 688, "color": "#0E6808" }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 1456, -288 ], "id": "72c16df2-30a4-441e-9b38-e14e62c43040", "name": "Sticky Note2" } ], "connections": { "Gmail Trigger": { "main": [ [ { "node": "Code - Normalizar correo MVP Sofía", "type": "main", "index": 0 } ] ] }, "IF - Es correo MVP Sofía": { "main": [ [ { "node": "HTTP Request - Flowise extraer caso", "type": "main", "index": 0 } ] ] }, "Code - Normalizar correo MVP Sofía": { "main": [ [ { "node": "IF - Es correo MVP Sofía", "type": "main", "index": 0 } ] ] }, "HTTP Request - Flowise extraer caso": { "main": [ [ { "node": "Code - Parsear JSON Flowise", "type": "main", "index": 0 } ] ] }, "Code - Parsear JSON Flowise": { "main": [ [ { "node": "If", "type": "main", "index": 0 } ] ] }, "If": { "main": [ [ { "node": "Code - Preparar Cypher Neo4j", "type": "main", "index": 0 } ] ] }, "Code - Preparar Cypher Neo4j": { "main": [ [ { "node": "HTTP Request - Crear caso en Neo4j", "type": "main", "index": 0 } ] ] }, "HTTP Request - Crear caso en Neo4j": { "main": [ [] ] } }, "authors": "Isaac Aracena", "name": "Version da03274d", "description": "", "autosaved": true, "workflowPublishHistory": [ { "createdAt": "2026-06-09T19:00:41.252Z", "id": 1273, "workflowId": "1ykpTM58gz0iOxvK", "versionId": "da03274d-aca0-4d78-be28-6543d5ecad92", "event": "activated", "userId": "0a88c0b1-928e-4412-896e-c5d1c99b2029" } ] } }