Files
cdc-brief-glm-v2/cdc-brief-glm-v2.json

4121 lines
178 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"updatedAt": "2026-06-29T23:45:56.725Z",
"createdAt": "2026-05-25T16:49:10.427Z",
"id": "Z8W67i7RfrhXRDFQ",
"name": "CDC Brief - GLM V2",
"description": null,
"active": true,
"isArchived": false,
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "cdc-brief-test-metadata",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-1488,
688
],
"id": "3ea7e4f5-8bf3-4591-953f-e9a853e871fe",
"name": "Webhook",
"webhookId": "0459b7fe-a6fc-47cb-bcf1-afadb9587124"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"leftValue": "={{$json.type}} ",
"rightValue": "pdf_document",
"operator": {
"type": "string",
"operation": "equals"
},
"id": "19494f94-55a5-4849-bdc0-e056140e371d"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "PDF"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "9c4aa96c-5b3c-46bb-a2f7-a10da4c807be",
"leftValue": "={{$json.type}}",
"rightValue": "image",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Imagen"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "3d1a3c24-4fe2-4239-9b64-2a33c3043fb0",
"leftValue": "={{$('Code in JavaScript').item.json.type}}",
"rightValue": "video",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Video"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "29941bad-2e02-4c26-bccb-115fbb2c4b57",
"leftValue": "={{$json.type}}",
"rightValue": "audio",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Audio"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "8ff5d5cf-26e7-4516-a154-0d7fa8855012",
"leftValue": "={{$json.type}}",
"rightValue": "text",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Texto"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "437f4ff8-8556-4f8f-b935-a55eda65afde",
"leftValue": "={{$json.type}}",
"rightValue": "other",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "other"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
-624,
576
],
"id": "93f970e0-be35-4a98-9ecd-b962e6549589",
"name": "Switch"
},
{
"parameters": {
"resource": "audio",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
800,
688
],
"id": "7b3d0637-9b3c-4daf-8615-87455ab23905",
"name": "Analyze audio",
"retryOnFail": false,
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"numberInputs": 3
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
3056,
736
],
"id": "5f1ad3e3-3ab2-40c8-a694-e7e5755322ce",
"name": "Merge"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"messages": {
"values": [
{
"content": "=Actúa como un asistente de inteligencia artificial especializado en interpretación y estructuración de briefs creativos, estratégicos y comerciales.\n\nTu tarea es analizar todo el contenido recibido y transformarlo en un brief profesional, claro, accionable y listo para revisión interna, edición en Google Docs y representación visual en HTML.\n\nInstrucciones de trabajo:\n\n1. Captura e interpretación\n- Lee y consolida toda la información disponible.\n- Si existe texto derivado del audio, úsalo como contexto principal.\n- Si hay documentos, imágenes o referencias visuales, intégralos como soporte complementario.\n- Extrae entidades clave, objetivos, requerimientos, restricciones, entregables, fechas, audiencias, marcas, productos y cualquier dato relevante.\n- Distingue claramente entre:\n - Tipo de requerimiento = categoría general de la solicitud.\n - Tipo de entregable = pedido específico o salida concreta esperada.\n\n2. Detección de vacíos\n- Detecta campos obligatorios ausentes.\n- Identifica detalles técnicos faltantes.\n- Señala contradicciones o inconsistencias.\n- Marca información ambigua o poco clara.\n- Advierte posibles riesgos para el desarrollo creativo, estratégico o de producción.\n\n3. Enriquecimiento y estructuración\n- Reorganiza la información y conviértela en un brief profesional con redacción clara, precisa y ejecutiva.\n- Mejora la redacción sin inventar datos no proporcionados.\n- Cuando algo no esté disponible, indícalo como “No especificado” o “Pendiente de definir”.\n- Si no existe un nombre explícito para el proyecto, genera un nombre breve y profesional usando únicamente los datos reales disponibles, sin inventar información nueva.\n\n4. Reglas importantes de salida\n- No repitas información redundante.\n- No inventes datos.\n- Prioriza claridad, orden y utilidad práctica.\n- Redacta con tono profesional y ejecutivo.\n- La salida debe sentirse como un brief listo para revisión interna.\n- Debes incluir explícitamente los campos “Nombre del proyecto”, “Cliente”, “Marca”, “País”, “Tipo de requerimiento” y “Tipo de entregable”.\n- No mezcles “Tipo de requerimiento” con “Tipo de entregable”.\n- Si cliente y marca son la misma cosa, puedes repetir el valor en ambos campos.\n- Si no puedes identificar uno de los dos, usa “No especificado”.\n\n5. Genera la salida exactamente en este formato:\n\n1. Resumen ejecutivo\n- Explica de forma breve y profesional de qué trata la solicitud, cuál es el objetivo y qué se espera desarrollar.\n\n2. Información clave del brief\n- Nombre del proyecto\n- Cliente\n- Marca\n- País\n- Producto / servicio\n- Tipo de requerimiento\n- Tipo de entregable\n- Objetivo principal\n- Contexto / antecedente\n- Audiencia objetivo\n- Mensaje clave\n- Entregables solicitados\n- Canales / formatos\n- Requerimientos técnicos\n- Referencias creativas o visuales\n- Fechas o tiempos relevantes\n- Presupuesto\n- Restricciones o consideraciones especiales\n\n3. Vacíos detectados\n- Lista clara de campos faltantes, ambiguos o contradictorios.\n\n4. Riesgos o alertas\n- Señala riesgos que puedan afectar la ejecución, creatividad, tiempos, costos o claridad del proyecto.\n\n5. Recomendaciones rápidas\n- Sugiere qué información adicional conviene completar o reforzar antes de aprobar el brief final.\n\n6. Nivel de completitud\n- Indica un porcentaje estimado de completitud del brief según la información recibida.\n\nContenido recibido:\n{{$json.combined_content}}"
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
3440,
752
],
"id": "2fb18bfd-e222-4107-a98c-fb6f3b6ab295",
"name": "Message a model",
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "ddc91110-0c3b-4ee7-a973-322a3d1c7c1e",
"name": "type",
"value": "audio",
"type": "string"
},
{
"id": "b667804d-e4b9-4225-9a6e-01d54067f0cf",
"name": "fileName",
"value": "={{$json.fileName || ''}}",
"type": "string"
},
{
"id": "4023fd4f-95ce-4571-845b-c7555c4efc88",
"name": "content_summary",
"value": "={{ $json.content.parts[0].text || '' }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1888,
672
],
"id": "a6b8547f-cd75-49b4-bf2f-cbe205a0d465",
"name": "Edit Fields1"
},
{
"parameters": {
"respondWith": "text",
"responseBody": "=Este es el enlace al folder con el contenido del brief:\n\nhttps://drive.google.com/drive/u/0/folders/{{ $('Create folder').first().json.id }}\n\nEn caso de algún vacío detectado, favor de ingresar al link y modificar el archivo.\n\nVacíos detectados:\n\n{{ $('Build Docs Requests').first().json.fields.VACIOS_DETECTADOS || \"Ninguno\" }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
7520,
688
],
"id": "fccc258d-d736-4d05-9f32-221921093e49",
"name": "Respond to Webhook"
},
{
"parameters": {
"resource": "image",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
816,
928
],
"id": "91da2758-fb1e-4a2e-ad21-e08b5c54a0a7",
"name": "Analyze an image",
"retryOnFail": false,
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"resource": "folder",
"name": "=Brief {{$now}}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"mode": "list",
"value": "root",
"cachedResultName": "/ (Root folder)"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
-1328,
688
],
"id": "7de162cd-4388-436e-b870-1de9fafad3a8",
"name": "Create folder",
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "738bec56-f577-41da-a35f-dc8346893e10",
"name": "fileName",
"value": "={{ $('Switch').item.json.fileName || '' }}",
"type": "string"
},
{
"id": "6df83b75-7e13-48db-a557-7ee85c52a2b2",
"name": "content_summary",
"value": "={{ $json.content?.parts?.[0]?.text || 'Imagen adjunta por el usuario. No se pudo generar una descripción automática en este intento.' }}",
"type": "string"
},
{
"id": "1a1db20c-3dd7-4776-9d97-46bd31213a87",
"name": "type",
"value": "image",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2080,
960
],
"id": "ba2df39a-fe08-4b73-9ece-d1c273a710dd",
"name": "Edit Fields2"
},
{
"parameters": {
"resource": "folder",
"operation": "share",
"folderNoRootId": {
"__rl": true,
"value": "={{ $('Create folder').first().json.id }}",
"mode": "id"
},
"permissionsUi": {
"permissionsValues": {
"role": "writer",
"type": "domain",
"domain": "gomezleemarketing.com"
}
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
7104,
688
],
"id": "8217b8c5-f406-45af-b929-94b9aa60d387",
"name": "Share folder",
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"name": "={{ $json.fileName }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $json.folderId }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
1200,
1392
],
"id": "9b472aac-ac5e-4e33-bef7-c676de10d699",
"name": "Upload file",
"executeOnce": false,
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"sendTo": "gmarrero@gomezleemarketing.com, obrisceno@gomezleemarketing.com, boliva@gomezleemarketing.com, areyes@gomezleemarketing.com",
"subject": "✏️ Nuevo Brief",
"message": "=<div style=\"display:none!important; visibility:hidden; opacity:0; color:transparent; height:0; width:0; max-height:0; max-width:0; overflow:hidden; mso-hide:all; font-size:1px; line-height:1px;\">\n Enviado por {{ $('Webhook').first().json.body.submittedByName || 'No especificado' }}\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n</div>\n\n<div style=\"margin:0; padding:0; background:#ffffff; font-family:Arial, Helvetica, sans-serif; color:#111827;\">\n <div style=\"max-width:640px; margin:0 auto; padding:18px 0;\">\n\n<div style=\"background:#ffffff; border-radius:20px; overflow:hidden; border:1px solid #e5e7eb;\">\n\n <div style=\"background:linear-gradient(135deg,#111827 0%,#1e1b4b 50%,#0e7490 100%); padding:32px 28px;\">\n <div style=\"font-size:26px; line-height:1.25; font-weight:900; color:#ffffff; margin:0;\">\n Se ha creado un nuevo brief\n </div>\n </div>\n\n <div style=\"padding:28px;\">\n <p style=\"margin:0 0 20px 0; font-size:16px; line-height:1.6; color:#111827;\">\n Hola Gerardo,\n </p>\n\n <p style=\"margin:0 0 24px 0; font-size:16px; line-height:1.7; color:#374151;\">\n Te informo que se ha creado el siguiente brief:\n </p>\n\n <div style=\"background:#f8fafc; border:1px solid #dbe3ef; border-radius:18px; padding:20px; margin-bottom:22px;\">\n <div style=\"font-size:12px; letter-spacing:0.12em; text-transform:uppercase; color:#64748b; font-weight:800; margin-bottom:14px;\">\n Carpeta del brief\n </div>\n\n <a href=\"https://drive.google.com/drive/u/0/folders/{{ $('Create folder').first().json.id }}\"\n style=\"display:inline-block; background:#1e1b4b; color:#ffffff; text-decoration:none; padding:13px 22px; border-radius:999px; font-size:14px; font-weight:800;\">\n Abrir carpeta en Google Drive\n </a>\n </div>\n\n <div style=\"border:1px solid #e5e7eb; border-radius:18px; overflow:hidden; margin-bottom:24px;\">\n <div style=\"background:#f9fafb; padding:14px 18px; border-bottom:1px solid #e5e7eb;\">\n <div style=\"font-size:12px; letter-spacing:0.12em; text-transform:uppercase; color:#6b7280; font-weight:800;\">\n Datos del envío\n </div>\n </div>\n\n <div style=\"padding:18px;\">\n <div style=\"margin-bottom:18px;\">\n <div style=\"font-size:13px; color:#6b7280; font-weight:700; margin-bottom:4px;\">\n Enviado por\n </div>\n\n <div style=\"font-size:16px; color:#111827; font-weight:400; line-height:1.4;\">\n {{ $('Webhook').first().json.body.submittedByName || 'No especificado' }}\n </div>\n </div>\n\n <div style=\"margin-bottom:18px;\">\n <div style=\"font-size:13px; color:#6b7280; font-weight:700; margin-bottom:4px;\">\n Correo\n </div>\n\n <a href=\"mailto:{{ $('Webhook').first().json.body.submittedByEmail || '' }}\"\n style=\"font-size:15px; color:#1d8cf8; font-weight:600; text-decoration:none; line-height:1.4;\">\n {{ $('Webhook').first().json.body.submittedByEmail || 'No especificado' }}\n </a>\n </div>\n\n <div>\n <div style=\"font-size:13px; color:#6b7280; font-weight:700; margin-bottom:4px;\">\n Fecha límite de entrega\n </div>\n\n <div style=\"font-size:16px; color:#111827; font-weight:400; line-height:1.4;\">\n {{ $('Preparar fecha de entrega').first().json.deliveryDateLabel || $('Preparar fecha de entrega').first().json.fechaEntregaTexto || $('Webhook').first().json.body.deliveryDateLabel || $('Webhook').first().json.body.fechaEntregaTexto || 'Por confirmar' }}\n </div>\n </div>\n </div>\n </div>\n\n <p style=\"margin:0; font-size:16px; line-height:1.6; color:#111827;\">\n Gracias,\n </p>\n </div>\n\n</div>\n\n\n </div>\n</div>\n",
"options": {
"senderName": "📢 HEY"
}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
7872,
688
],
"id": "14e8c6c6-d1c1-48f1-80c6-efb34420f33d",
"name": "Send a message",
"webhookId": "2a920b9f-c697-4a4e-9c69-0bf0749c2661",
"credentials": {
"gmailOAuth2": {
"id": "cehQBe6X1BQLmcD9",
"name": "Gmail account - Isaac"
}
}
},
{
"parameters": {
"operation": "copy",
"fileId": {
"__rl": true,
"value": "1UuCLClREoC_9OvT407B36nHJ1x4l418Q0dEwgGccws4",
"mode": "list",
"cachedResultName": "CDC_Brief_GLM_V2",
"cachedResultUrl": "https://docs.google.com/document/d/1UuCLClREoC_9OvT407B36nHJ1x4l418Q0dEwgGccws4/edit?usp=drivesdk"
},
"name": "={{ $('Build Names').first().json.docName }}",
"sameFolder": false,
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $('Create folder').first().json.id }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
6432,
688
],
"id": "e2dc1490-d155-4def-bb21-39195d6e8908",
"name": "Copy template",
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"jsCode": "const text = String($json.googleDocsText || '').replace(/\\r\\n/g, '\\n').trim();\nconst ANO = String(new Date().getFullYear());\n\nconst baseItems = $('Code in JavaScript').all();\n\nconst urlRegex = /https?:\\/\\/[^\\s<>\"')]+/gi;\nconst linksSet = new Set();\nconst attachmentsSet = new Set();\n\nfor (const item of baseItems) {\n const json = item.json || {};\n\n const textValue = String(json.text || '').trim();\n const matches = textValue.match(urlRegex) || [];\n for (const url of matches) {\n linksSet.add(url);\n }\n\n const fileName = String(json.fileName || '').trim();\n if (fileName) {\n attachmentsSet.add(fileName);\n }\n}\n\nconst support = {\n enlacesReferenciaText: linksSet.size\n ? Array.from(linksSet).map(url => `• ${url}`).join('\\n')\n : 'No se compartieron enlaces de referencia online.',\n\n adjuntosRecibidosText: attachmentsSet.size\n ? Array.from(attachmentsSet).map(name => `• ${name}`).join('\\n')\n : 'No se recibieron adjuntos adicionales.'\n};\n\nfunction getNodeJson(nodeName) {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n}\n\nfunction sectionBetween(startRe, endRe) {\n const lines = text.split('\\n');\n let start = -1;\n let end = lines.length;\n\n for (let i = 0; i < lines.length; i++) {\n if (startRe.test(lines[i].trim())) {\n start = i + 1;\n break;\n }\n }\n\n if (start === -1) return '';\n\n if (endRe) {\n for (let i = start; i < lines.length; i++) {\n if (endRe.test(lines[i].trim())) {\n end = i;\n break;\n }\n }\n }\n\n return lines.slice(start, end).join('\\n').trim();\n}\n\nfunction normalizeLabel(str) {\n return String(str || '')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction cleanValue(value, fallback = 'No especificado') {\n const lines = String(value || '')\n .split('\\n')\n .map(line => line.trim())\n .filter(Boolean);\n\n return lines.length ? lines.join('\\n') : fallback;\n}\n\nfunction formatMultiline(value, fallback = 'No especificado') {\n const lines = String(value || '')\n .split('\\n')\n .map(line => line.trim())\n .filter(Boolean);\n\n if (!lines.length) return fallback;\n\n return lines\n .map(line => {\n if (/^[•*\\-]\\s*/.test(line)) {\n return `• ${line.replace(/^[•*\\-]\\s*/, '').trim()}`;\n }\n return line;\n })\n .join('\\n');\n}\n\nfunction bullets(sectionText, fallback = 'No especificado') {\n const items = String(sectionText || '')\n .split('\\n')\n .map(line => line.replace(/^\\s*[•*\\-]\\s*/, '').trim())\n .filter(Boolean);\n\n if (!items.length) return fallback;\n\n return items.map(item => `• ${item}`).join('\\n');\n}\n\nfunction completion(sectionText) {\n const match = String(sectionText || '').match(/(\\d{1,3}\\s*%)/);\n if (match) return match[1].replace(/\\s+/g, '');\n\n const cleaned = String(sectionText || '').trim();\n return cleaned || 'No especificado';\n}\n\nfunction buildFechasTiemposRelevantes(fechaEntrega, fechasDetectadas) {\n const fecha = String(fechaEntrega || '').trim() || 'Por confirmar';\n const fechas = String(fechasDetectadas || '').trim();\n\n const entregaLine = `• Fecha límite de entrega: ${fecha}.`;\n\n const fechasNormalizadas = normalizeLabel(fechas)\n .replace(/[.:;]+$/g, '')\n .trim();\n\n const noHayFechasDetectadas =\n !fechas ||\n fechasNormalizadas === 'no especificado' ||\n fechasNormalizadas === 'no especificada' ||\n fechasNormalizadas === 'no se especifico' ||\n fechasNormalizadas === 'no se especifica' ||\n fechasNormalizadas === 'no indicado' ||\n fechasNormalizadas === 'no indicada';\n\n if (noHayFechasDetectadas) {\n return entregaLine;\n }\n\n return `${entregaLine}\\n${fechas}`;\n}\n\nconst fechaEntregaNode = getNodeJson('Preparar fecha de entrega');\n\nconst fechaEntrega =\n fechaEntregaNode.deliveryDateLabel ||\n fechaEntregaNode.fechaEntregaTexto ||\n 'Por confirmar';\n\nconst infoSection = sectionBetween(\n /^2\\.\\s*Informaci[oó]n clave del brief$/i,\n /^3\\.\\s*Vac[ií]os\\s+detectados$/i\n);\n\nconst labelMap = {\n 'Nombre del proyecto': 'NOMBRE_PROYECTO',\n 'Cliente': 'CLIENTE',\n 'Marca': 'MARCA',\n 'País': 'PAIS',\n 'Pais': 'PAIS',\n 'Producto / servicio': 'PRODUCTO_SERVICIO',\n 'Tipo de requerimiento': 'TIPO_REQUERIMIENTO',\n 'Tipo de entregable': 'TIPO_ENTREGABLE',\n 'Objetivo principal': 'OBJETIVO_PRINCIPAL',\n 'Contexto / antecedente': 'CONTEXTO_ANTECEDENTE',\n 'Audiencia objetivo': 'AUDIENCIA_OBJETIVO',\n 'Mensaje clave': 'MENSAJE_CLAVE',\n 'Entregables solicitados': 'ENTREGABLES',\n 'Canales / formatos': 'CANALES_FORMATOS',\n 'Requerimientos técnicos': 'REQUERIMIENTOS_TECNICOS',\n 'Referencias creativas o visuales': 'REFERENCIAS_VISUALES',\n 'Fechas o tiempos relevantes': 'FECHAS_TIEMPOS_RELEVANTES',\n 'Fechas / tiempos relevantes': 'FECHAS_TIEMPOS_RELEVANTES',\n 'Presupuesto': 'PRESUPUESTO',\n 'Restricciones o consideraciones especiales': 'RESTRICCIONES'\n};\n\nconst normalizedLabelMap = Object.fromEntries(\n Object.entries(labelMap).map(([label, key]) => [normalizeLabel(label), key])\n);\n\nconst info = {};\nlet currentKey = null;\n\nfor (const rawLine of infoSection.split('\\n')) {\n const line = rawLine.trim();\n if (!line) continue;\n\n const match = line.match(/^(?:[•*\\-]\\s*)?(.+?):\\s*(.*)$/);\n\n if (match) {\n const labelCandidate = normalizeLabel(match[1]);\n const mappedKey = normalizedLabelMap[labelCandidate];\n\n if (mappedKey) {\n currentKey = mappedKey;\n info[currentKey] = match[2].trim();\n continue;\n }\n }\n\n if (currentKey) {\n info[currentKey] = [info[currentKey], line].filter(Boolean).join('\\n');\n }\n}\n\nconst CLIENTE = cleanValue(info.CLIENTE);\nconst MARCA = cleanValue(info.MARCA);\n\nfunction buildMarcaCliente(cliente, marca) {\n const c = cleanValue(cliente);\n const m = cleanValue(marca);\n\n if (c === 'No especificado' && m === 'No especificado') return 'No especificado';\n if (c === 'No especificado') return m;\n if (m === 'No especificado') return c;\n\n const nc = normalizeLabel(c);\n const nm = normalizeLabel(m);\n\n if (nc === nm) return c.length >= m.length ? c : m;\n\n if (nc.includes(nm) || nm.includes(nc)) {\n return c.length >= m.length ? c : m;\n }\n\n return `${m} / ${c}`;\n}\n\nconst MARCA_CLIENTE = buildMarcaCliente(CLIENTE, MARCA);\n\nconst fechasDetectadas = formatMultiline(info.FECHAS_TIEMPOS_RELEVANTES);\n\nconst fields = {\n ANO,\n NOMBRE_PROYECTO: cleanValue(info.NOMBRE_PROYECTO),\n CLIENTE,\n MARCA,\n MARCA_CLIENTE,\n\n PAIS: cleanValue(info.PAIS),\n RESUMEN_EJECUTIVO: sectionBetween(\n /^1\\.\\s*Resumen ejecutivo$/i,\n /^2\\.\\s*Informaci[oó]n clave del brief$/i\n ) || 'No especificado',\n\n PRODUCTO_SERVICIO: cleanValue(info.PRODUCTO_SERVICIO),\n TIPO_REQUERIMIENTO: cleanValue(info.TIPO_REQUERIMIENTO),\n TIPO_ENTREGABLE: cleanValue(info.TIPO_ENTREGABLE),\n OBJETIVO_PRINCIPAL: cleanValue(info.OBJETIVO_PRINCIPAL),\n CONTEXTO_ANTECEDENTE: cleanValue(info.CONTEXTO_ANTECEDENTE),\n AUDIENCIA_OBJETIVO: cleanValue(info.AUDIENCIA_OBJETIVO),\n MENSAJE_CLAVE: cleanValue(info.MENSAJE_CLAVE),\n\n ENTREGABLES: formatMultiline(info.ENTREGABLES),\n CANALES_FORMATOS: formatMultiline(info.CANALES_FORMATOS),\n REQUERIMIENTOS_TECNICOS: formatMultiline(info.REQUERIMIENTOS_TECNICOS),\n REFERENCIAS_VISUALES: formatMultiline(info.REFERENCIAS_VISUALES),\n\n FECHAS_TIEMPOS_RELEVANTES: buildFechasTiemposRelevantes(\n fechaEntrega,\n fechasDetectadas\n ),\n\n PRESUPUESTO: cleanValue(info.PRESUPUESTO),\n RESTRICCIONES: formatMultiline(info.RESTRICCIONES),\n\n VACIOS_DETECTADOS: bullets(\n sectionBetween(\n /^3\\.\\s*Vac[ií]os\\s+detectados$/i,\n /^4\\.\\s*Riesgos o alertas$/i\n )\n ),\n\n RIESGOS_ALERTAS: bullets(\n sectionBetween(\n /^4\\.\\s*Riesgos o alertas$/i,\n /^5\\.\\s*Recomendaciones rápidas$/i\n )\n ),\n\n RECOMENDACIONES_RAPIDAS: bullets(\n sectionBetween(\n /^5\\.\\s*Recomendaciones rápidas$/i,\n /^6\\.\\s*Nivel de completitud$/i\n )\n ),\n\n NIVEL_COMPLETITUD: completion(\n sectionBetween(\n /^6\\.\\s*Nivel de completitud$/i,\n null\n )\n ),\n\n ENLACES_REFERENCIA: support.enlacesReferenciaText || 'No se compartieron enlaces de referencia online.',\n ADJUNTOS_RECIBIDOS: support.adjuntosRecibidosText || 'No se recibieron adjuntos adicionales.'\n};\n\nconst requests = Object.entries(fields).map(([key, value]) => ({\n replaceAllText: {\n containsText: {\n text: `{{${key}}}`,\n matchCase: true,\n },\n replaceText: value,\n },\n}));\n\nreturn [\n {\n json: {\n fields,\n requests,\n },\n },\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5328,
688
],
"id": "57bef68a-7648-489c-8ffb-a7e5652a80af",
"name": "Build Docs Requests"
},
{
"parameters": {
"method": "POST",
"url": "=https://docs.googleapis.com/v1/documents/{{ $('Copy template').first().json.id }}:batchUpdate",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "googleDocsOAuth2Api",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { \"requests\": $('Build Docs Requests').first().json.requests } }}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
6784,
688
],
"id": "377a0388-d30c-4735-971e-9b4b79a07bea",
"name": "HTTP Request",
"credentials": {
"googleDocsOAuth2Api": {
"id": "2bGzUIxSb9LeUeNO",
"name": "Isaac - Google Docs"
}
}
},
{
"parameters": {
"jsCode": "const f = $json.fields || {};\n\nfunction cleanPart(value, fallback = 'No especificado') {\n const text = String(value || '').trim();\n return text || fallback;\n}\n\nfunction safePart(value, fallback = 'No especificado') {\n return cleanPart(value, fallback)\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[\\\\/:*?\"<>|]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/\\s/g, '-');\n}\n\nconst ano = safePart(f.ANO);\nconst cliente = safePart(f.CLIENTE);\nconst marca = safePart(f.MARCA);\nconst pais = safePart(f.PAIS);\nconst tipoRequerimiento = safePart(f.TIPO_REQUERIMIENTO);\n\nconst baseName = [\n ano,\n cliente,\n marca,\n pais,\n tipoRequerimiento\n].join('_');\n\nreturn [\n {\n json: {\n folderName: baseName,\n docName: baseName,\n htmlName: `${baseName}.html`,\n pdfName: `${baseName}.pdf`,\n projectName: cleanPart(f.NOMBRE_PROYECTO),\n fields: f\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5600,
688
],
"id": "d69b87b6-d228-42ba-b43a-aec0b527bf09",
"name": "Build Names"
},
{
"parameters": {
"operation": "update",
"fileId": {
"__rl": true,
"value": "={{ $('Create folder').first().json.id }}",
"mode": "id"
},
"newUpdatedFileName": "={{ $('Build Names').first().json.folderName }}",
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
5936,
688
],
"id": "c04d4432-4081-437b-b9a5-2d28387ba2d5",
"name": "Rename folder",
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"content": "## ENTRADA DEL FLUJO\n\n• Recibe la solicitud del frontend\n• Crea carpeta base en Google Drive\n• Esa carpeta guardará brief y adjuntos\n\nEntradas:\nvoz, imágenes, PDFs y otros archivos",
"height": 400,
"width": 304,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1504,
480
],
"id": "c1fd447e-d16c-45d3-9d9b-6a382517ae8a",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## CLASIFICACIÓN Y ENRUTAMIENTO\n\n• Identifica tipo de entrada\n• Separa: PDF, imagen, audio, texto y otros\n• Prepara cada rama para su procesamiento\n• También detecta enlaces en texto",
"height": 512,
"width": 692,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1088,
400
],
"id": "c4e13e49-10d1-4b5f-aff3-e3485aa6e333",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## GUARDADO DE ADJUNTOS\n\n• Guarda los archivos originales del usuario\n• Incluye PDFs, imágenes y otros soportes\n• No todos se analizan\n• Sí quedan preservados en la carpeta final",
"height": 320,
"width": 672,
"color": "#433D3D"
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
720,
1232
],
"id": "4dce857e-1292-43c5-b5dc-a95f33a1d9dd",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "## ANÁLISIS DE INSUMOS\n\n• PDF: se analiza con Gemini\n• Audio: se transcribe / resume\n• Imagen: se interpreta\n• Si no hay PDF, el flujo continúa\n• Cada salida se normaliza en Edit Fields",
"height": 1216,
"width": 2752,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-320,
-32
],
"id": "d96b5f33-53f1-4d3d-a146-bf3fcaf3a166",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "## CONSOLIDACIÓN DEL CONTEXTO\n\n• Une documento, audio e imagen\n• Construye un contexto único\n• Envía todo al modelo\n• El modelo genera el brief estructurado",
"height": 576,
"width": 2000,
"color": "#9E2929"
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
2704,
480
],
"id": "5ea54f56-8749-46a2-aff5-04920de7f51c",
"name": "Sticky Note4"
},
{
"parameters": {
"content": "## POSTPROCESO DEL BRIEF\n\n• Limpia la salida del modelo\n• Extrae campos del brief\n• Construye placeholders\n• Genera nombres finales\n• Renombra la carpeta creada al inicio",
"height": 352,
"width": 1252
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
4816,
496
],
"id": "1206c7e8-be96-45ce-acf7-2f21d61608a5",
"name": "Sticky Note5"
},
{
"parameters": {
"content": "## SALIDA FINAL\n\n• Copia la plantilla oficial\n• Inserta la información del brief\n• Comparte la carpeta\n• Devuelve el enlace por webhook\n• Envía el correo",
"height": 384,
"width": 2340,
"color": "#3F360C"
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
6192,
496
],
"id": "b7587368-4628-4885-a1c1-c0be75e4dba5",
"name": "Sticky Note6"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "f34e6067-5572-44ef-bac9-738212957d5c",
"leftValue": "={{ $json.pdfMissing || false }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
48,
208
],
"id": "2779ddc8-7631-417e-8800-c4a604014b4b",
"name": "If PDF missing"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "be678498-4e99-45bf-acb7-b7ade22fabb5",
"name": "fileName",
"value": "''",
"type": "string"
},
{
"id": "e9a77032-adbd-462c-bb2e-20f267496a8e",
"name": "content_summary",
"value": "No se adjuntó PDF. Continúa el flujo con audio e imagen.",
"type": "string"
},
{
"id": "bf85223a-197a-4a28-b3bf-064dc0fe8300",
"name": "type",
"value": "document",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
912,
-16
],
"id": "72c12649-c5c8-47d3-a571-cbb3f73ceae6",
"name": "PDF opcional omitido"
},
{
"parameters": {
"resource": "document",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
448,
352
],
"id": "0b5b8ccb-ebcc-4b12-984b-a336264bd4ae",
"name": "Analyze document",
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "0b8567da-0fd5-45c3-9f4e-071036d665bb",
"name": "fileName",
"value": "={{ $json.fileName || $('Switch').item.json.fileName || '' }}",
"type": "string"
},
{
"id": "7af7edfc-6c2a-4183-ac0f-f5e16e925fff",
"name": "content_summary",
"value": "={{ $json.content?.parts?.[0]?.text || $json.content_summary || '' }}",
"type": "string"
},
{
"id": "6447843f-472f-4249-ac49-bf63ed6cb04c",
"name": "type",
"value": "document",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1952,
176
],
"id": "f5280257-72a5-4721-8073-013e65b187ba",
"name": "Edit Fields documento"
},
{
"parameters": {
"jsCode": "const items = $input.all();\nreturn items;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-96,
208
],
"id": "62375717-01d5-4dc3-b106-7304ebe20c3d",
"name": "Procesar documentos"
},
{
"parameters": {
"jsCode": "const webhookItems = $('Webhook').all();\nconst out = [];\n\nlet hasPdfDocument = false;\n\nfor (const webhookItem of webhookItems) {\n const json = webhookItem.json || {};\n const body = json.body || {};\n const binary = webhookItem.binary || {};\n\n const text =\n body.text ||\n body.message ||\n body.caption ||\n body.prompt ||\n json.text ||\n json.message ||\n '';\n\n if (typeof text === 'string' && text.trim() !== '') {\n out.push({\n json: {\n type: 'text',\n text: text.trim(),\n }\n });\n }\n\n for (const key of Object.keys(binary)) {\n const file = binary[key];\n const mime = String(file.mimeType || '').toLowerCase();\n const ext = String(file.fileExtension || '').toLowerCase();\n const fileName = String(file.fileName || '').toLowerCase();\n\n let type = 'attachment_only';\n\n const isImage = mime.startsWith('image/');\n const isAudio = mime.startsWith('audio/');\n const isPdf =\n mime.includes('pdf') ||\n ext === 'pdf' ||\n fileName.endsWith('.pdf');\n\n if (isImage) {\n type = 'image';\n } else if (isAudio) {\n type = 'audio';\n } else if (isPdf) {\n type = 'pdf_document';\n hasPdfDocument = true;\n } else {\n type = 'attachment_only';\n }\n\n out.push({\n json: {\n type,\n binaryKey: key,\n mimeType: file.mimeType || '',\n fileName: file.fileName || '',\n fileExtension: file.fileExtension || '',\n fileSize: file.fileSize || '',\n },\n binary: {\n data: file\n }\n });\n }\n}\n\nif (!hasPdfDocument) {\n out.push({\n json: {\n type: 'pdf_document',\n pdfMissing: true,\n fileName: '',\n content_summary: 'No se adjuntó PDF. Continúa el flujo con audio e imagen.'\n }\n });\n}\n\nreturn out;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-800,
688
],
"id": "4ee568a2-916f-47ec-8517-a2b3ba65a9cc",
"name": "Code in JavaScript"
},
{
"parameters": {
"jsCode": "const folderId = $('Create folder').first().json.id;\nconst items = $input.all();\n\nconst output = [];\n\nfor (const item of items) {\n const binaries = item.binary || {};\n\n for (const key of Object.keys(binaries)) {\n const file = binaries[key];\n\n output.push({\n json: {\n folderId,\n fileName: file.fileName || key,\n mimeType: file.mimeType || '',\n },\n binary: {\n data: file\n }\n });\n }\n}\n\nreturn output;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
784,
1392
],
"id": "aaab3f40-a654-4289-8325-d7eab6cd73dd",
"name": "Code in JavaScript3"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nif (!items.length) {\n return [{\n json: {\n combined_content: 'No se recibió contenido para resumir.'\n }\n }];\n}\n\nconst combined = items.map((item, i) => {\n const type = item.json.type || 'other';\n const fileName = item.json.fileName || '';\n const summary = item.json.content_summary || '';\n\n return [\n `Entrada ${i + 1}`,\n `Tipo: ${type}`,\n fileName ? `Archivo: ${fileName}` : null,\n `Contenido:`,\n summary\n ].filter(Boolean).join('\\n');\n}).join('\\n\\n====================\\n\\n');\n\nreturn [\n {\n json: {\n combined_content: combined\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
3216,
752
],
"id": "cf149c50-8821-4bb9-bcd3-451fefdbdb8f",
"name": "Code in JavaScript1"
},
{
"parameters": {
"jsCode": "const raw = $('Message a model').item.json.content.parts[0].text || '';\n\nlet formatted = raw\n .replace(/\\r\\n/g, '\\n')\n .replace(/^---$/gm, '')\n .replace(/^# (.*)$/gm, '\\n$1\\n')\n .replace(/^## (.*)$/gm, '\\n$1\\n')\n .replace(/^### (.*)$/gm, '\\n$1\\n')\n .replace(/\\*\\*(.*?)\\*\\*/g, '$1')\n .replace(/^\\*\\s+/gm, '• ')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n\nformatted = `BRIEF PROFESIONAL\\n\\n${formatted}`;\n\nreturn [\n {\n json: {\n googleDocsText: formatted\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
4896,
688
],
"id": "ccd4c5dd-fdd4-4a94-bc57-fea7c40e30f9",
"name": "Code in JavaScript4"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nreturn items.map((item, index) => ({\n json: {\n ...item.json,\n _fallbackIndex: index,\n },\n binary: item.binary,\n}));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
240,
288
],
"id": "5ab6e790-bdd8-47db-ad31-ded716007eb1",
"name": "Backup document input"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nreturn items.map((item, index) => ({\n json: {\n ...item.json,\n _fallbackIndex: index,\n },\n binary: item.binary,\n}));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
576,
640
],
"id": "b38d2104-40d0-465f-8ed8-2f20d1828037",
"name": "Backup audio input"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nreturn items.map((item, index) => ({\n json: {\n ...item.json,\n _fallbackIndex: index,\n },\n binary: item.binary,\n}));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
624,
928
],
"id": "4938540d-2300-4dab-ba47-476ba38ac879",
"name": "Backup image input"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "52ff398d-d5fa-4329-8755-03578f1f3965",
"leftValue": "={{ !$json.content?.parts?.[0]?.text }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1040,
944
],
"id": "3484866a-ae5e-4d99-bfa5-b93828aa4902",
"name": "IF imagen gratis falló"
},
{
"parameters": {
"resource": "image",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
1584,
768
],
"id": "4c653881-cb9a-451a-8d26-51424ebef3bd",
"name": "Analyze an image - Pago",
"retryOnFail": false,
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"jsCode": "const failedItems = $input.all();\nconst originalItems = $('Backup image input').all();\n\nreturn failedItems.map((failedItem, index) => {\n let originalIndex = failedItem.json?._fallbackIndex;\n\n if (originalIndex === undefined && failedItem.pairedItem) {\n if (Array.isArray(failedItem.pairedItem)) {\n originalIndex = failedItem.pairedItem[0]?.item;\n } else {\n originalIndex = failedItem.pairedItem.item;\n }\n }\n\n if (originalIndex === undefined) {\n originalIndex = index;\n }\n\n const original = originalItems[originalIndex] || originalItems[index] || originalItems[0];\n\n if (!original?.binary?.data) {\n throw new Error(`No se encontró binary.data para recuperar la imagen en el índice ${originalIndex}`);\n }\n\n return {\n json: {\n ...original.json,\n fallbackUsed: true,\n fallbackReason: failedItem.json?.error || 'Error en API gratis',\n },\n binary: original.binary,\n };\n});"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1280,
768
],
"id": "11b28396-2d34-4978-b562-5190d20908bf",
"name": "Recuperar binario imagen"
},
{
"parameters": {
"resource": "audio",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
1520,
464
],
"id": "90fa8b77-56ae-4f57-b268-ba5b74a9d5f0",
"name": "Analyze audio - Pago",
"retryOnFail": false,
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "a0d47fed-e52c-4882-bd89-c7833b214111",
"leftValue": "={{ !$json.content?.parts?.[0]?.text }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
976,
656
],
"id": "6096066c-9335-4c00-8b4f-b36936ad0f42",
"name": "If audio gratis falló"
},
{
"parameters": {
"jsCode": "const failedItems = $input.all();\nconst originalItems = $('Backup audio input').all();\n\nreturn failedItems.map((failedItem, index) => {\n let originalIndex = failedItem.json?._fallbackIndex;\n\n if (originalIndex === undefined && failedItem.pairedItem) {\n if (Array.isArray(failedItem.pairedItem)) {\n originalIndex = failedItem.pairedItem[0]?.item;\n } else {\n originalIndex = failedItem.pairedItem.item;\n }\n }\n\n if (originalIndex === undefined) {\n originalIndex = index;\n }\n\n const original = originalItems[originalIndex] || originalItems[index] || originalItems[0];\n\n if (!original?.binary?.data) {\n throw new Error(`No se encontró binary.data para recuperar el audio en el índice ${originalIndex}`);\n }\n\n return {\n json: {\n ...original.json,\n fallbackUsed: true,\n fallbackReason: failedItem.json?.error || 'Error en API gratis',\n },\n binary: original.binary,\n };\n});"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1312,
464
],
"id": "e10409e3-1d67-4c8e-800b-7d9dcfd14ca6",
"name": "Recuperar binario audio"
},
{
"parameters": {
"resource": "document",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
1104,
160
],
"id": "49feacc4-b313-4c20-913f-05fe2246abf3",
"name": "Analyze document - Pago",
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "033b7aa0-7c40-4f6a-9056-83094b6451f0",
"leftValue": "={{ !$json.content?.parts?.[0]?.text }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
640,
352
],
"id": "92a589b2-52b9-442b-93b8-fda2ed0a00c3",
"name": "IF documento gratis falló"
},
{
"parameters": {
"jsCode": "const failedItems = $input.all();\nconst originalItems = $('Backup document input').all();\n\nreturn failedItems.map((failedItem, index) => {\n let originalIndex = failedItem.json?._fallbackIndex;\n\n if (originalIndex === undefined && failedItem.pairedItem) {\n if (Array.isArray(failedItem.pairedItem)) {\n originalIndex = failedItem.pairedItem[0]?.item;\n } else {\n originalIndex = failedItem.pairedItem.item;\n }\n }\n\n if (originalIndex === undefined) {\n originalIndex = index;\n }\n\n const original = originalItems[originalIndex] || originalItems[index] || originalItems[0];\n\n if (!original?.binary?.data) {\n throw new Error(`No se encontró binary.data para recuperar el documento en el índice ${originalIndex}`);\n }\n\n return {\n json: {\n ...original.json,\n fallbackUsed: true,\n fallbackReason: failedItem.json?.error || 'Error en API gratis',\n },\n binary: original.binary,\n };\n});"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
880,
176
],
"id": "5a3884bb-fb6f-4bfa-89bd-1b281cb7555b",
"name": "Recuperar binario documento"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"messages": {
"values": [
{
"content": "=Actúa como un asistente de inteligencia artificial especializado en interpretación y estructuración de briefs creativos, estratégicos y comerciales.\n\nTu tarea es analizar todo el contenido recibido y transformarlo en un brief profesional, claro, accionable y listo para revisión interna, edición en Google Docs y representación visual en HTML.\n\nInstrucciones de trabajo:\n\n1. Captura e interpretación\n- Lee y consolida toda la información disponible.\n- Si existe texto derivado del audio, úsalo como contexto principal.\n- Si hay documentos, imágenes o referencias visuales, intégralos como soporte complementario.\n- Extrae entidades clave, objetivos, requerimientos, restricciones, entregables, fechas, audiencias, marcas, productos y cualquier dato relevante.\n- Distingue claramente entre:\n - Tipo de requerimiento = categoría general de la solicitud.\n - Tipo de entregable = pedido específico o salida concreta esperada.\n\n2. Detección de vacíos\n- Detecta campos obligatorios ausentes.\n- Identifica detalles técnicos faltantes.\n- Señala contradicciones o inconsistencias.\n- Marca información ambigua o poco clara.\n- Advierte posibles riesgos para el desarrollo creativo, estratégico o de producción.\n\n3. Enriquecimiento y estructuración\n- Reorganiza la información y conviértela en un brief profesional con redacción clara, precisa y ejecutiva.\n- Mejora la redacción sin inventar datos no proporcionados.\n- Cuando algo no esté disponible, indícalo como “No especificado” o “Pendiente de definir”.\n- Si no existe un nombre explícito para el proyecto, genera un nombre breve y profesional usando únicamente los datos reales disponibles, sin inventar información nueva.\n\n4. Reglas importantes de salida\n- No repitas información redundante.\n- No inventes datos.\n- Prioriza claridad, orden y utilidad práctica.\n- Redacta con tono profesional y ejecutivo.\n- La salida debe sentirse como un brief listo para revisión interna.\n- Debes incluir explícitamente los campos “Nombre del proyecto”, “Cliente”, “Marca”, “País”, “Tipo de requerimiento” y “Tipo de entregable”.\n- No mezcles “Tipo de requerimiento” con “Tipo de entregable”.\n- Si cliente y marca son la misma cosa, puedes repetir el valor en ambos campos.\n- Si no puedes identificar uno de los dos, usa “No especificado”.\n\n5. Genera la salida exactamente en este formato:\n\n1. Resumen ejecutivo\n- Explica de forma breve y profesional de qué trata la solicitud, cuál es el objetivo y qué se espera desarrollar.\n\n2. Información clave del brief\n- Nombre del proyecto\n- Cliente\n- Marca\n- País\n- Producto / servicio\n- Tipo de requerimiento\n- Tipo de entregable\n- Objetivo principal\n- Contexto / antecedente\n- Audiencia objetivo\n- Mensaje clave\n- Entregables solicitados\n- Canales / formatos\n- Requerimientos técnicos\n- Referencias creativas o visuales\n- Fechas o tiempos relevantes\n- Presupuesto\n- Restricciones o consideraciones especiales\n\n3. Vacíos detectados\n- Lista clara de campos faltantes, ambiguos o contradictorios.\n\n4. Riesgos o alertas\n- Señala riesgos que puedan afectar la ejecución, creatividad, tiempos, costos o claridad del proyecto.\n\n5. Recomendaciones rápidas\n- Sugiere qué información adicional conviene completar o reforzar antes de aprobar el brief final.\n\n6. Nivel de completitud\n- Indica un porcentaje estimado de completitud del brief según la información recibida.\n\nContenido recibido:\n{{$json.combined_content}}"
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
4128,
592
],
"id": "a3c326a3-4e0b-4ae6-8a0f-6eca61648af3",
"name": "Message a model - Pago",
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "0ccf935c-14e8-4e1b-b7db-cb994c970733",
"leftValue": "={{ !$json.content?.parts?.[0]?.text }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
3872,
736
],
"id": "0e8aa2ac-ef86-4451-adcb-d6bda7c07146",
"name": "If"
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "1h_d4l4FYkLvyy_Zp7bEQFKpy5hx6pVj9xZ98mjMHHSU",
"mode": "list",
"cachedResultName": "CDC Brief - Base para Fulgencio",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1h_d4l4FYkLvyy_Zp7bEQFKpy5hx6pVj9xZ98mjMHHSU/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Briefs",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1h_d4l4FYkLvyy_Zp7bEQFKpy5hx6pVj9xZ98mjMHHSU/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"fecha_creacion": "={{ $json.fecha_creacion }}",
"brief_id": "={{ $json.brief_id }}",
"nombre_carpeta": "={{ $json.nombre_carpeta }}",
"enviado_por": "={{ $json.enviado_por }}",
"correo": "={{ $json.correo }}",
"link_carpeta_drive": "={{ $json.link_carpeta_drive }}",
"link_documento_brief": "={{ $json.link_documento_brief }}",
"estado": "={{ $json.estado }}",
"cantidad_audios": "={{ $json.cantidad_audios }}",
"cantidad_imagenes": "={{ $json.cantidad_imagenes }}",
"cantidad_documentos": "={{ $json.cantidad_documentos }}",
"cantidad_links": "={{ $json.cantidad_links }}",
"resumen_chatbot": "={{ $json.resumen_chatbot }}",
"texto_brief": "={{ $json.texto_brief }}",
"texto_busqueda": "={{ $json.texto_busqueda }}",
"fecha_entrega": "={{ $('Preparar fecha de entrega').first().json.deliveryDateLabel }}"
},
"matchingColumns": [],
"schema": [
{
"id": "fecha_creacion",
"displayName": "fecha_creacion",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "brief_id",
"displayName": "brief_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "nombre_carpeta",
"displayName": "nombre_carpeta",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "enviado_por",
"displayName": "enviado_por",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "correo",
"displayName": "correo",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "link_carpeta_drive",
"displayName": "link_carpeta_drive",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "link_documento_brief",
"displayName": "link_documento_brief",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "estado",
"displayName": "estado",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "cantidad_audios",
"displayName": "cantidad_audios",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "cantidad_imagenes",
"displayName": "cantidad_imagenes",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "cantidad_documentos",
"displayName": "cantidad_documentos",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "cantidad_links",
"displayName": "cantidad_links",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "resumen_chatbot",
"displayName": "resumen_chatbot",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "texto_brief",
"displayName": "texto_brief",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "texto_busqueda",
"displayName": "texto_busqueda",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "fecha_entrega",
"displayName": "fecha_entrega",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
8320,
688
],
"id": "2b332c13-0621-4eb0-a9b7-27e820963746",
"name": "Agregar fila a Sheet Fulgencio",
"retryOnFail": false,
"alwaysOutputData": true,
"credentials": {
"googleSheetsOAuth2Api": {
"id": "K0hDZh3a85MpOHCs",
"name": "Google Sheets account 2"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "function getNodeJson(nodeName) {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n}\n\nfunction getNodeBinary(nodeName) {\n try {\n return $(nodeName).first().binary || {};\n } catch (error) {\n return {};\n }\n}\n\nfunction cleanText(value) {\n return String(value || '')\n .replace(/\\r\\n/g, '\\n')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction limitText(value, maxLength = 45000) {\n const text = String(value || '');\n return text.length > maxLength ? text.slice(0, maxLength) : text;\n}\n\nfunction parseLinks(value) {\n if (!value) return [];\n\n if (Array.isArray(value)) {\n return value.filter(Boolean).map(String);\n }\n\n return String(value)\n .split(/\\n|,|\\s+/)\n .map(link => link.trim())\n .filter(link => /^https?:\\/\\//i.test(link));\n}\n\nconst webhookItem = getNodeJson('Webhook');\nconst webhookBinary = getNodeBinary('Webhook');\n\nconst body =\n webhookItem.body && Object.keys(webhookItem.body).length\n ? webhookItem.body\n : webhookItem;\n\nconst folderNode = getNodeJson('Create folder');\nconst docNode = getNodeJson('Copy template');\nconst finalTextNode = getNodeJson('Code in JavaScript4');\nconst fechaEntregaNode = getNodeJson('Preparar fecha de entrega');\n\nconst folderId = folderNode.id || '';\nconst folderName = folderNode.name || '';\nconst docId = docNode.id || '';\n\nconst folderUrl = folderId\n ? `https://drive.google.com/drive/u/0/folders/${folderId}`\n : '';\n\nconst docUrl = docId\n ? `https://docs.google.com/document/d/${docId}/edit`\n : '';\n\nconst submittedByName =\n body.submittedByName ||\n body.displayName ||\n body.name ||\n body.email ||\n 'No especificado';\n\nconst submittedByEmail =\n body.submittedByEmail ||\n body.email ||\n 'No especificado';\n\nconst fechaEntrega =\n fechaEntregaNode.deliveryDateLabel ||\n fechaEntregaNode.fechaEntregaTexto ||\n body.deliveryDateLabel ||\n body.fechaEntregaTexto ||\n 'Por confirmar';\n\nconst finalBriefText =\n finalTextNode.googleDocsText ||\n finalTextNode.text ||\n finalTextNode.content ||\n '';\n\nconst files = Object.values(webhookBinary || {});\n\nconst audioCount = files.filter(file =>\n String(file.mimeType || '').startsWith('audio/')\n).length;\n\nconst imageCount = files.filter(file =>\n String(file.mimeType || '').startsWith('image/')\n).length;\n\nconst documentCount = files.filter(file => {\n const mime = String(file.mimeType || '');\n return (\n mime.includes('pdf') ||\n mime.includes('word') ||\n mime.includes('excel') ||\n mime.includes('spreadsheet') ||\n mime.includes('presentation') ||\n mime.includes('powerpoint') ||\n mime.includes('officedocument')\n );\n}).length;\n\nconst links = [\n ...parseLinks(body.links),\n ...parseLinks(body.enlaces),\n ...parseLinks(body.referenceLinks),\n ...parseLinks(body.referencias),\n ...parseLinks(body.urls),\n];\n\nconst fechaCreacion = new Date().toISOString();\n\nconst resumenChatbot = cleanText(finalBriefText).slice(0, 1200);\n\nconst textoBusqueda = [\n `Brief CDC`,\n `Fecha: ${fechaCreacion}`,\n `Fecha límite de entrega: ${fechaEntrega}`,\n `Enviado por: ${submittedByName}`,\n `Correo: ${submittedByEmail}`,\n `Carpeta: ${folderUrl}`,\n `Documento: ${docUrl}`,\n `Nombre carpeta: ${folderName}`,\n '',\n finalBriefText,\n].join('\\n');\n\nreturn [\n {\n json: {\n fecha_creacion: fechaCreacion,\n brief_id: folderId || `${Date.now()}`,\n nombre_carpeta: folderName,\n enviado_por: submittedByName,\n correo: submittedByEmail,\n fecha_entrega: fechaEntrega,\n link_carpeta_drive: folderUrl,\n link_documento_brief: docUrl,\n estado: 'Nuevo',\n cantidad_audios: audioCount,\n cantidad_imagenes: imageCount,\n cantidad_documentos: documentCount,\n cantidad_links: links.length,\n resumen_chatbot: resumenChatbot,\n texto_brief: limitText(finalBriefText),\n texto_busqueda: limitText(textoBusqueda),\n },\n },\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
8112,
688
],
"id": "b44ed7d4-7333-44b6-b46f-d3d50a922eae",
"name": "Preparar fila para Sheet Fulgencio"
},
{
"parameters": {
"jsCode": "const inputItem = $input.first();\nconst inputJson = inputItem.json || {};\n\nconst webhookJson = $('Webhook').first().json || {};\nconst body = webhookJson.body || {};\n\nfunction formatDateEs(dateString) {\n if (!dateString) return '';\n\n const value = String(dateString).trim();\n\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n return value;\n }\n\n const [year, month, day] = value.split('-');\n const date = new Date(Number(year), Number(month) - 1, Number(day));\n\n if (Number.isNaN(date.getTime())) {\n return value;\n }\n\n return date.toLocaleDateString('es-DO', {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n });\n}\n\nconst deliveryDateRaw =\n body.deliveryDate ||\n body.fechaEntrega ||\n '';\n\nconst deliveryDateStatusRaw =\n body.deliveryDateStatus ||\n body.fechaEntregaEstado ||\n '';\n\nconst deliveryDateLabelFromFrontend =\n body.deliveryDateLabel ||\n body.fechaEntregaTexto ||\n '';\n\nconst hasDate =\n deliveryDateRaw !== undefined &&\n deliveryDateRaw !== null &&\n String(deliveryDateRaw).trim() !== '';\n\nconst normalizedStatus = String(deliveryDateStatusRaw || '').toLowerCase();\nconst normalizedLabel = String(deliveryDateLabelFromFrontend || '').toLowerCase();\n\nconst isPending =\n !hasDate &&\n (\n normalizedStatus === 'pending' ||\n normalizedStatus === 'por_confirmar' ||\n normalizedStatus === 'por confirmar' ||\n normalizedLabel === 'por confirmar'\n );\n\nconst deliveryDateLabel = isPending\n ? 'Por confirmar'\n : hasDate\n ? formatDateEs(deliveryDateRaw)\n : deliveryDateLabelFromFrontend || 'Por confirmar';\n\nreturn [\n {\n json: {\n ...inputJson,\n deliveryDateRaw,\n deliveryDateStatus: isPending ? 'pending' : 'defined',\n deliveryDateLabel,\n fechaEntregaTexto: deliveryDateLabel,\n fechaEntregaEstado: isPending ? 'por_confirmar' : 'definida',\n },\n binary: inputItem.binary,\n },\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1008,
688
],
"id": "dabcfbdd-a1db-4241-950e-6259a27586b9",
"name": "Preparar fecha de entrega"
},
{
"parameters": {
"jsCode": "const item = $input.first();\nconst json = item.json || {};\n\nconst fechaNode = $('Preparar fecha de entrega').first().json || {};\n\nconst deliveryDateLabel =\n fechaNode.deliveryDateLabel ||\n fechaNode.fechaEntregaTexto ||\n json.deliveryDateLabel ||\n json.fechaEntregaTexto ||\n 'Por confirmar';\n\nconst deliveryLine = `• Fecha límite de entrega: ${deliveryDateLabel}.`;\n\nlet googleDocsText = json.googleDocsText || '';\n\nif (!googleDocsText) {\n return [\n {\n json: {\n ...json,\n deliveryDateLabel,\n fechaEntregaTexto: deliveryDateLabel,\n },\n binary: item.binary,\n },\n ];\n}\n\n// Evita duplicar la fecha si el nodo se ejecuta más de una vez\ngoogleDocsText = googleDocsText.replace(\n /^\\s*[•-]?\\s*Fecha límite de entrega:.*$/gim,\n ''\n);\n\n// Inserta la fecha justo debajo de \"Fechas / tiempos relevantes\"\nconst fechasHeadingRegex = /(Fechas\\s*\\/\\s*tiempos\\s+relevantes\\s*:?\\s*)/i;\n\nif (fechasHeadingRegex.test(googleDocsText)) {\n googleDocsText = googleDocsText.replace(\n fechasHeadingRegex,\n `$1\\n${deliveryLine}\\n`\n );\n} else {\n // Si por alguna razón la sección no aparece, la agrega al final\n googleDocsText += `\\n\\nFechas / tiempos relevantes\\n${deliveryLine}`;\n}\n\n// Limpieza básica de saltos de línea excesivos\ngoogleDocsText = googleDocsText\n .replace(/\\n{4,}/g, '\\n\\n\\n')\n .trim();\n\nreturn [\n {\n json: {\n ...json,\n googleDocsText,\n deliveryDateLabel,\n fechaEntregaTexto: deliveryDateLabel,\n },\n binary: item.binary,\n },\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5104,
688
],
"id": "084305a2-38dd-4b3f-b900-6a3f7c26c69c",
"name": "Agregar fecha al Google Doc"
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Create folder",
"type": "main",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "Procesar documentos",
"type": "main",
"index": 0
}
],
[
{
"node": "Backup image input",
"type": "main",
"index": 0
}
],
[],
[
{
"node": "Backup audio input",
"type": "main",
"index": 0
}
]
]
},
"Analyze audio": {
"main": [
[
{
"node": "If audio gratis falló",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Code in JavaScript1",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields1": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Message a model": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Analyze an image": {
"main": [
[
{
"node": "IF imagen gratis falló",
"type": "main",
"index": 0
}
]
]
},
"Create folder": {
"main": [
[
{
"node": "Preparar fecha de entrega",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields2": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 2
}
]
]
},
"Share folder": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Respond to Webhook": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
},
"Copy template": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Build Docs Requests": {
"main": [
[
{
"node": "Build Names",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Share folder",
"type": "main",
"index": 0
}
]
]
},
"Build Names": {
"main": [
[
{
"node": "Rename folder",
"type": "main",
"index": 0
}
]
]
},
"Rename folder": {
"main": [
[
{
"node": "Copy template",
"type": "main",
"index": 0
}
]
]
},
"If PDF missing": {
"main": [
[
{
"node": "PDF opcional omitido",
"type": "main",
"index": 0
}
],
[
{
"node": "Backup document input",
"type": "main",
"index": 0
}
]
]
},
"Analyze document": {
"main": [
[
{
"node": "IF documento gratis falló",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields documento": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"PDF opcional omitido": {
"main": [
[
{
"node": "Edit Fields documento",
"type": "main",
"index": 0
}
]
]
},
"Procesar documentos": {
"main": [
[
{
"node": "If PDF missing",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Code in JavaScript3",
"type": "main",
"index": 0
},
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript3": {
"main": [
[
{
"node": "Upload file",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript1": {
"main": [
[
{
"node": "Message a model",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript4": {
"main": [
[
{
"node": "Agregar fecha al Google Doc",
"type": "main",
"index": 0
}
]
]
},
"Backup document input": {
"main": [
[
{
"node": "Analyze document",
"type": "main",
"index": 0
}
]
]
},
"Backup audio input": {
"main": [
[
{
"node": "Analyze audio",
"type": "main",
"index": 0
}
]
]
},
"Backup image input": {
"main": [
[
{
"node": "Analyze an image",
"type": "main",
"index": 0
}
]
]
},
"IF imagen gratis falló": {
"main": [
[
{
"node": "Recuperar binario imagen",
"type": "main",
"index": 0
}
],
[
{
"node": "Edit Fields2",
"type": "main",
"index": 0
}
]
]
},
"Recuperar binario imagen": {
"main": [
[
{
"node": "Analyze an image - Pago",
"type": "main",
"index": 0
}
]
]
},
"Analyze an image - Pago": {
"main": [
[
{
"node": "Edit Fields2",
"type": "main",
"index": 0
}
]
]
},
"If audio gratis falló": {
"main": [
[
{
"node": "Recuperar binario audio",
"type": "main",
"index": 0
}
],
[
{
"node": "Edit Fields1",
"type": "main",
"index": 0
}
]
]
},
"Recuperar binario audio": {
"main": [
[
{
"node": "Analyze audio - Pago",
"type": "main",
"index": 0
}
]
]
},
"Analyze audio - Pago": {
"main": [
[
{
"node": "Edit Fields1",
"type": "main",
"index": 0
}
]
]
},
"IF documento gratis falló": {
"main": [
[
{
"node": "Recuperar binario documento",
"type": "main",
"index": 0
}
],
[
{
"node": "Edit Fields documento",
"type": "main",
"index": 0
}
]
]
},
"Recuperar binario documento": {
"main": [
[
{
"node": "Analyze document - Pago",
"type": "main",
"index": 0
}
]
]
},
"Analyze document - Pago": {
"main": [
[
{
"node": "Edit Fields documento",
"type": "main",
"index": 0
}
]
]
},
"If": {
"main": [
[
{
"node": "Message a model - Pago",
"type": "main",
"index": 0
}
],
[
{
"node": "Code in JavaScript4",
"type": "main",
"index": 0
}
]
]
},
"Message a model - Pago": {
"main": [
[
{
"node": "Code in JavaScript4",
"type": "main",
"index": 0
}
]
]
},
"Send a message": {
"main": [
[
{
"node": "Preparar fila para Sheet Fulgencio",
"type": "main",
"index": 0
}
]
]
},
"Preparar fila para Sheet Fulgencio": {
"main": [
[
{
"node": "Agregar fila a Sheet Fulgencio",
"type": "main",
"index": 0
}
]
]
},
"Preparar fecha de entrega": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Agregar fecha al Google Doc": {
"main": [
[
{
"node": "Build Docs Requests",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"timeSavedMode": "fixed",
"errorWorkflow": "puF4LUczoSz3hcek",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
},
"staticData": null,
"meta": {
"templateCredsSetupCompleted": true
},
"versionId": "ead282aa-1eee-42b4-a685-aae69f3f25e3",
"activeVersionId": "ead282aa-1eee-42b4-a685-aae69f3f25e3",
"versionCounter": 172,
"triggerCount": 1,
"shared": [
{
"updatedAt": "2026-05-25T16:49:10.432Z",
"createdAt": "2026-05-25T16:49:10.432Z",
"role": "workflow:owner",
"workflowId": "Z8W67i7RfrhXRDFQ",
"projectId": "PJpTANzTXIFibWsW",
"project": {
"updatedAt": "2026-04-22T14:25:09.686Z",
"createdAt": "2026-04-22T14:22:54.790Z",
"id": "PJpTANzTXIFibWsW",
"name": "Isaac Aracena <iaracena@gomezleemarketing.com>",
"type": "personal",
"icon": null,
"description": null,
"creatorId": "0a88c0b1-928e-4412-896e-c5d1c99b2029"
}
}
],
"tags": [],
"activeVersion": {
"updatedAt": "2026-06-30T00:14:41.000Z",
"createdAt": "2026-06-29T23:45:56.728Z",
"versionId": "ead282aa-1eee-42b4-a685-aae69f3f25e3",
"workflowId": "Z8W67i7RfrhXRDFQ",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "cdc-brief-test-metadata",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-1488,
688
],
"id": "3ea7e4f5-8bf3-4591-953f-e9a853e871fe",
"name": "Webhook",
"webhookId": "0459b7fe-a6fc-47cb-bcf1-afadb9587124"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"leftValue": "={{$json.type}} ",
"rightValue": "pdf_document",
"operator": {
"type": "string",
"operation": "equals"
},
"id": "19494f94-55a5-4849-bdc0-e056140e371d"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "PDF"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "9c4aa96c-5b3c-46bb-a2f7-a10da4c807be",
"leftValue": "={{$json.type}}",
"rightValue": "image",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Imagen"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "3d1a3c24-4fe2-4239-9b64-2a33c3043fb0",
"leftValue": "={{$('Code in JavaScript').item.json.type}}",
"rightValue": "video",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Video"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "29941bad-2e02-4c26-bccb-115fbb2c4b57",
"leftValue": "={{$json.type}}",
"rightValue": "audio",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Audio"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "8ff5d5cf-26e7-4516-a154-0d7fa8855012",
"leftValue": "={{$json.type}}",
"rightValue": "text",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Texto"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "437f4ff8-8556-4f8f-b935-a55eda65afde",
"leftValue": "={{$json.type}}",
"rightValue": "other",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "other"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
-624,
576
],
"id": "93f970e0-be35-4a98-9ecd-b962e6549589",
"name": "Switch"
},
{
"parameters": {
"resource": "audio",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
800,
688
],
"id": "7b3d0637-9b3c-4daf-8615-87455ab23905",
"name": "Analyze audio",
"retryOnFail": false,
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"numberInputs": 3
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
3056,
736
],
"id": "5f1ad3e3-3ab2-40c8-a694-e7e5755322ce",
"name": "Merge"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"messages": {
"values": [
{
"content": "=Actúa como un asistente de inteligencia artificial especializado en interpretación y estructuración de briefs creativos, estratégicos y comerciales.\n\nTu tarea es analizar todo el contenido recibido y transformarlo en un brief profesional, claro, accionable y listo para revisión interna, edición en Google Docs y representación visual en HTML.\n\nInstrucciones de trabajo:\n\n1. Captura e interpretación\n- Lee y consolida toda la información disponible.\n- Si existe texto derivado del audio, úsalo como contexto principal.\n- Si hay documentos, imágenes o referencias visuales, intégralos como soporte complementario.\n- Extrae entidades clave, objetivos, requerimientos, restricciones, entregables, fechas, audiencias, marcas, productos y cualquier dato relevante.\n- Distingue claramente entre:\n - Tipo de requerimiento = categoría general de la solicitud.\n - Tipo de entregable = pedido específico o salida concreta esperada.\n\n2. Detección de vacíos\n- Detecta campos obligatorios ausentes.\n- Identifica detalles técnicos faltantes.\n- Señala contradicciones o inconsistencias.\n- Marca información ambigua o poco clara.\n- Advierte posibles riesgos para el desarrollo creativo, estratégico o de producción.\n\n3. Enriquecimiento y estructuración\n- Reorganiza la información y conviértela en un brief profesional con redacción clara, precisa y ejecutiva.\n- Mejora la redacción sin inventar datos no proporcionados.\n- Cuando algo no esté disponible, indícalo como “No especificado” o “Pendiente de definir”.\n- Si no existe un nombre explícito para el proyecto, genera un nombre breve y profesional usando únicamente los datos reales disponibles, sin inventar información nueva.\n\n4. Reglas importantes de salida\n- No repitas información redundante.\n- No inventes datos.\n- Prioriza claridad, orden y utilidad práctica.\n- Redacta con tono profesional y ejecutivo.\n- La salida debe sentirse como un brief listo para revisión interna.\n- Debes incluir explícitamente los campos “Nombre del proyecto”, “Cliente”, “Marca”, “País”, “Tipo de requerimiento” y “Tipo de entregable”.\n- No mezcles “Tipo de requerimiento” con “Tipo de entregable”.\n- Si cliente y marca son la misma cosa, puedes repetir el valor en ambos campos.\n- Si no puedes identificar uno de los dos, usa “No especificado”.\n\n5. Genera la salida exactamente en este formato:\n\n1. Resumen ejecutivo\n- Explica de forma breve y profesional de qué trata la solicitud, cuál es el objetivo y qué se espera desarrollar.\n\n2. Información clave del brief\n- Nombre del proyecto\n- Cliente\n- Marca\n- País\n- Producto / servicio\n- Tipo de requerimiento\n- Tipo de entregable\n- Objetivo principal\n- Contexto / antecedente\n- Audiencia objetivo\n- Mensaje clave\n- Entregables solicitados\n- Canales / formatos\n- Requerimientos técnicos\n- Referencias creativas o visuales\n- Fechas o tiempos relevantes\n- Presupuesto\n- Restricciones o consideraciones especiales\n\n3. Vacíos detectados\n- Lista clara de campos faltantes, ambiguos o contradictorios.\n\n4. Riesgos o alertas\n- Señala riesgos que puedan afectar la ejecución, creatividad, tiempos, costos o claridad del proyecto.\n\n5. Recomendaciones rápidas\n- Sugiere qué información adicional conviene completar o reforzar antes de aprobar el brief final.\n\n6. Nivel de completitud\n- Indica un porcentaje estimado de completitud del brief según la información recibida.\n\nContenido recibido:\n{{$json.combined_content}}"
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
3440,
752
],
"id": "2fb18bfd-e222-4107-a98c-fb6f3b6ab295",
"name": "Message a model",
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "ddc91110-0c3b-4ee7-a973-322a3d1c7c1e",
"name": "type",
"value": "audio",
"type": "string"
},
{
"id": "b667804d-e4b9-4225-9a6e-01d54067f0cf",
"name": "fileName",
"value": "={{$json.fileName || ''}}",
"type": "string"
},
{
"id": "4023fd4f-95ce-4571-845b-c7555c4efc88",
"name": "content_summary",
"value": "={{ $json.content.parts[0].text || '' }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1888,
672
],
"id": "a6b8547f-cd75-49b4-bf2f-cbe205a0d465",
"name": "Edit Fields1"
},
{
"parameters": {
"respondWith": "text",
"responseBody": "=Este es el enlace al folder con el contenido del brief:\n\nhttps://drive.google.com/drive/u/0/folders/{{ $('Create folder').first().json.id }}\n\nEn caso de algún vacío detectado, favor de ingresar al link y modificar el archivo.\n\nVacíos detectados:\n\n{{ $('Build Docs Requests').first().json.fields.VACIOS_DETECTADOS || \"Ninguno\" }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
7520,
688
],
"id": "fccc258d-d736-4d05-9f32-221921093e49",
"name": "Respond to Webhook"
},
{
"parameters": {
"resource": "image",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
816,
928
],
"id": "91da2758-fb1e-4a2e-ad21-e08b5c54a0a7",
"name": "Analyze an image",
"retryOnFail": false,
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"resource": "folder",
"name": "=Brief {{$now}}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"mode": "list",
"value": "root",
"cachedResultName": "/ (Root folder)"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
-1328,
688
],
"id": "7de162cd-4388-436e-b870-1de9fafad3a8",
"name": "Create folder",
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "738bec56-f577-41da-a35f-dc8346893e10",
"name": "fileName",
"value": "={{ $('Switch').item.json.fileName || '' }}",
"type": "string"
},
{
"id": "6df83b75-7e13-48db-a557-7ee85c52a2b2",
"name": "content_summary",
"value": "={{ $json.content?.parts?.[0]?.text || 'Imagen adjunta por el usuario. No se pudo generar una descripción automática en este intento.' }}",
"type": "string"
},
{
"id": "1a1db20c-3dd7-4776-9d97-46bd31213a87",
"name": "type",
"value": "image",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2080,
960
],
"id": "ba2df39a-fe08-4b73-9ece-d1c273a710dd",
"name": "Edit Fields2"
},
{
"parameters": {
"resource": "folder",
"operation": "share",
"folderNoRootId": {
"__rl": true,
"value": "={{ $('Create folder').first().json.id }}",
"mode": "id"
},
"permissionsUi": {
"permissionsValues": {
"role": "writer",
"type": "domain",
"domain": "gomezleemarketing.com"
}
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
7104,
688
],
"id": "8217b8c5-f406-45af-b929-94b9aa60d387",
"name": "Share folder",
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"name": "={{ $json.fileName }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $json.folderId }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
1200,
1392
],
"id": "9b472aac-ac5e-4e33-bef7-c676de10d699",
"name": "Upload file",
"executeOnce": false,
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"sendTo": "gmarrero@gomezleemarketing.com, obrisceno@gomezleemarketing.com, boliva@gomezleemarketing.com, areyes@gomezleemarketing.com",
"subject": "✏️ Nuevo Brief",
"message": "=<div style=\"display:none!important; visibility:hidden; opacity:0; color:transparent; height:0; width:0; max-height:0; max-width:0; overflow:hidden; mso-hide:all; font-size:1px; line-height:1px;\">\n Enviado por {{ $('Webhook').first().json.body.submittedByName || 'No especificado' }}\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;\n</div>\n\n<div style=\"margin:0; padding:0; background:#ffffff; font-family:Arial, Helvetica, sans-serif; color:#111827;\">\n <div style=\"max-width:640px; margin:0 auto; padding:18px 0;\">\n\n<div style=\"background:#ffffff; border-radius:20px; overflow:hidden; border:1px solid #e5e7eb;\">\n\n <div style=\"background:linear-gradient(135deg,#111827 0%,#1e1b4b 50%,#0e7490 100%); padding:32px 28px;\">\n <div style=\"font-size:26px; line-height:1.25; font-weight:900; color:#ffffff; margin:0;\">\n Se ha creado un nuevo brief\n </div>\n </div>\n\n <div style=\"padding:28px;\">\n <p style=\"margin:0 0 20px 0; font-size:16px; line-height:1.6; color:#111827;\">\n Hola Gerardo,\n </p>\n\n <p style=\"margin:0 0 24px 0; font-size:16px; line-height:1.7; color:#374151;\">\n Te informo que se ha creado el siguiente brief:\n </p>\n\n <div style=\"background:#f8fafc; border:1px solid #dbe3ef; border-radius:18px; padding:20px; margin-bottom:22px;\">\n <div style=\"font-size:12px; letter-spacing:0.12em; text-transform:uppercase; color:#64748b; font-weight:800; margin-bottom:14px;\">\n Carpeta del brief\n </div>\n\n <a href=\"https://drive.google.com/drive/u/0/folders/{{ $('Create folder').first().json.id }}\"\n style=\"display:inline-block; background:#1e1b4b; color:#ffffff; text-decoration:none; padding:13px 22px; border-radius:999px; font-size:14px; font-weight:800;\">\n Abrir carpeta en Google Drive\n </a>\n </div>\n\n <div style=\"border:1px solid #e5e7eb; border-radius:18px; overflow:hidden; margin-bottom:24px;\">\n <div style=\"background:#f9fafb; padding:14px 18px; border-bottom:1px solid #e5e7eb;\">\n <div style=\"font-size:12px; letter-spacing:0.12em; text-transform:uppercase; color:#6b7280; font-weight:800;\">\n Datos del envío\n </div>\n </div>\n\n <div style=\"padding:18px;\">\n <div style=\"margin-bottom:18px;\">\n <div style=\"font-size:13px; color:#6b7280; font-weight:700; margin-bottom:4px;\">\n Enviado por\n </div>\n\n <div style=\"font-size:16px; color:#111827; font-weight:400; line-height:1.4;\">\n {{ $('Webhook').first().json.body.submittedByName || 'No especificado' }}\n </div>\n </div>\n\n <div style=\"margin-bottom:18px;\">\n <div style=\"font-size:13px; color:#6b7280; font-weight:700; margin-bottom:4px;\">\n Correo\n </div>\n\n <a href=\"mailto:{{ $('Webhook').first().json.body.submittedByEmail || '' }}\"\n style=\"font-size:15px; color:#1d8cf8; font-weight:600; text-decoration:none; line-height:1.4;\">\n {{ $('Webhook').first().json.body.submittedByEmail || 'No especificado' }}\n </a>\n </div>\n\n <div>\n <div style=\"font-size:13px; color:#6b7280; font-weight:700; margin-bottom:4px;\">\n Fecha límite de entrega\n </div>\n\n <div style=\"font-size:16px; color:#111827; font-weight:400; line-height:1.4;\">\n {{ $('Preparar fecha de entrega').first().json.deliveryDateLabel || $('Preparar fecha de entrega').first().json.fechaEntregaTexto || $('Webhook').first().json.body.deliveryDateLabel || $('Webhook').first().json.body.fechaEntregaTexto || 'Por confirmar' }}\n </div>\n </div>\n </div>\n </div>\n\n <p style=\"margin:0; font-size:16px; line-height:1.6; color:#111827;\">\n Gracias,\n </p>\n </div>\n\n</div>\n\n\n </div>\n</div>\n",
"options": {
"senderName": "📢 HEY"
}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
7872,
688
],
"id": "14e8c6c6-d1c1-48f1-80c6-efb34420f33d",
"name": "Send a message",
"webhookId": "2a920b9f-c697-4a4e-9c69-0bf0749c2661",
"credentials": {
"gmailOAuth2": {
"id": "cehQBe6X1BQLmcD9",
"name": "Gmail account - Isaac"
}
}
},
{
"parameters": {
"operation": "copy",
"fileId": {
"__rl": true,
"value": "1UuCLClREoC_9OvT407B36nHJ1x4l418Q0dEwgGccws4",
"mode": "list",
"cachedResultName": "CDC_Brief_GLM_V2",
"cachedResultUrl": "https://docs.google.com/document/d/1UuCLClREoC_9OvT407B36nHJ1x4l418Q0dEwgGccws4/edit?usp=drivesdk"
},
"name": "={{ $('Build Names').first().json.docName }}",
"sameFolder": false,
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $('Create folder').first().json.id }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
6432,
688
],
"id": "e2dc1490-d155-4def-bb21-39195d6e8908",
"name": "Copy template",
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"jsCode": "const text = String($json.googleDocsText || '').replace(/\\r\\n/g, '\\n').trim();\nconst ANO = String(new Date().getFullYear());\n\nconst baseItems = $('Code in JavaScript').all();\n\nconst urlRegex = /https?:\\/\\/[^\\s<>\"')]+/gi;\nconst linksSet = new Set();\nconst attachmentsSet = new Set();\n\nfor (const item of baseItems) {\n const json = item.json || {};\n\n const textValue = String(json.text || '').trim();\n const matches = textValue.match(urlRegex) || [];\n for (const url of matches) {\n linksSet.add(url);\n }\n\n const fileName = String(json.fileName || '').trim();\n if (fileName) {\n attachmentsSet.add(fileName);\n }\n}\n\nconst support = {\n enlacesReferenciaText: linksSet.size\n ? Array.from(linksSet).map(url => `• ${url}`).join('\\n')\n : 'No se compartieron enlaces de referencia online.',\n\n adjuntosRecibidosText: attachmentsSet.size\n ? Array.from(attachmentsSet).map(name => `• ${name}`).join('\\n')\n : 'No se recibieron adjuntos adicionales.'\n};\n\nfunction getNodeJson(nodeName) {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n}\n\nfunction sectionBetween(startRe, endRe) {\n const lines = text.split('\\n');\n let start = -1;\n let end = lines.length;\n\n for (let i = 0; i < lines.length; i++) {\n if (startRe.test(lines[i].trim())) {\n start = i + 1;\n break;\n }\n }\n\n if (start === -1) return '';\n\n if (endRe) {\n for (let i = start; i < lines.length; i++) {\n if (endRe.test(lines[i].trim())) {\n end = i;\n break;\n }\n }\n }\n\n return lines.slice(start, end).join('\\n').trim();\n}\n\nfunction normalizeLabel(str) {\n return String(str || '')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction cleanValue(value, fallback = 'No especificado') {\n const lines = String(value || '')\n .split('\\n')\n .map(line => line.trim())\n .filter(Boolean);\n\n return lines.length ? lines.join('\\n') : fallback;\n}\n\nfunction formatMultiline(value, fallback = 'No especificado') {\n const lines = String(value || '')\n .split('\\n')\n .map(line => line.trim())\n .filter(Boolean);\n\n if (!lines.length) return fallback;\n\n return lines\n .map(line => {\n if (/^[•*\\-]\\s*/.test(line)) {\n return `• ${line.replace(/^[•*\\-]\\s*/, '').trim()}`;\n }\n return line;\n })\n .join('\\n');\n}\n\nfunction bullets(sectionText, fallback = 'No especificado') {\n const items = String(sectionText || '')\n .split('\\n')\n .map(line => line.replace(/^\\s*[•*\\-]\\s*/, '').trim())\n .filter(Boolean);\n\n if (!items.length) return fallback;\n\n return items.map(item => `• ${item}`).join('\\n');\n}\n\nfunction completion(sectionText) {\n const match = String(sectionText || '').match(/(\\d{1,3}\\s*%)/);\n if (match) return match[1].replace(/\\s+/g, '');\n\n const cleaned = String(sectionText || '').trim();\n return cleaned || 'No especificado';\n}\n\nfunction buildFechasTiemposRelevantes(fechaEntrega, fechasDetectadas) {\n const fecha = String(fechaEntrega || '').trim() || 'Por confirmar';\n const fechas = String(fechasDetectadas || '').trim();\n\n const entregaLine = `• Fecha límite de entrega: ${fecha}.`;\n\n const fechasNormalizadas = normalizeLabel(fechas)\n .replace(/[.:;]+$/g, '')\n .trim();\n\n const noHayFechasDetectadas =\n !fechas ||\n fechasNormalizadas === 'no especificado' ||\n fechasNormalizadas === 'no especificada' ||\n fechasNormalizadas === 'no se especifico' ||\n fechasNormalizadas === 'no se especifica' ||\n fechasNormalizadas === 'no indicado' ||\n fechasNormalizadas === 'no indicada';\n\n if (noHayFechasDetectadas) {\n return entregaLine;\n }\n\n return `${entregaLine}\\n${fechas}`;\n}\n\nconst fechaEntregaNode = getNodeJson('Preparar fecha de entrega');\n\nconst fechaEntrega =\n fechaEntregaNode.deliveryDateLabel ||\n fechaEntregaNode.fechaEntregaTexto ||\n 'Por confirmar';\n\nconst infoSection = sectionBetween(\n /^2\\.\\s*Informaci[oó]n clave del brief$/i,\n /^3\\.\\s*Vac[ií]os\\s+detectados$/i\n);\n\nconst labelMap = {\n 'Nombre del proyecto': 'NOMBRE_PROYECTO',\n 'Cliente': 'CLIENTE',\n 'Marca': 'MARCA',\n 'País': 'PAIS',\n 'Pais': 'PAIS',\n 'Producto / servicio': 'PRODUCTO_SERVICIO',\n 'Tipo de requerimiento': 'TIPO_REQUERIMIENTO',\n 'Tipo de entregable': 'TIPO_ENTREGABLE',\n 'Objetivo principal': 'OBJETIVO_PRINCIPAL',\n 'Contexto / antecedente': 'CONTEXTO_ANTECEDENTE',\n 'Audiencia objetivo': 'AUDIENCIA_OBJETIVO',\n 'Mensaje clave': 'MENSAJE_CLAVE',\n 'Entregables solicitados': 'ENTREGABLES',\n 'Canales / formatos': 'CANALES_FORMATOS',\n 'Requerimientos técnicos': 'REQUERIMIENTOS_TECNICOS',\n 'Referencias creativas o visuales': 'REFERENCIAS_VISUALES',\n 'Fechas o tiempos relevantes': 'FECHAS_TIEMPOS_RELEVANTES',\n 'Fechas / tiempos relevantes': 'FECHAS_TIEMPOS_RELEVANTES',\n 'Presupuesto': 'PRESUPUESTO',\n 'Restricciones o consideraciones especiales': 'RESTRICCIONES'\n};\n\nconst normalizedLabelMap = Object.fromEntries(\n Object.entries(labelMap).map(([label, key]) => [normalizeLabel(label), key])\n);\n\nconst info = {};\nlet currentKey = null;\n\nfor (const rawLine of infoSection.split('\\n')) {\n const line = rawLine.trim();\n if (!line) continue;\n\n const match = line.match(/^(?:[•*\\-]\\s*)?(.+?):\\s*(.*)$/);\n\n if (match) {\n const labelCandidate = normalizeLabel(match[1]);\n const mappedKey = normalizedLabelMap[labelCandidate];\n\n if (mappedKey) {\n currentKey = mappedKey;\n info[currentKey] = match[2].trim();\n continue;\n }\n }\n\n if (currentKey) {\n info[currentKey] = [info[currentKey], line].filter(Boolean).join('\\n');\n }\n}\n\nconst CLIENTE = cleanValue(info.CLIENTE);\nconst MARCA = cleanValue(info.MARCA);\n\nfunction buildMarcaCliente(cliente, marca) {\n const c = cleanValue(cliente);\n const m = cleanValue(marca);\n\n if (c === 'No especificado' && m === 'No especificado') return 'No especificado';\n if (c === 'No especificado') return m;\n if (m === 'No especificado') return c;\n\n const nc = normalizeLabel(c);\n const nm = normalizeLabel(m);\n\n if (nc === nm) return c.length >= m.length ? c : m;\n\n if (nc.includes(nm) || nm.includes(nc)) {\n return c.length >= m.length ? c : m;\n }\n\n return `${m} / ${c}`;\n}\n\nconst MARCA_CLIENTE = buildMarcaCliente(CLIENTE, MARCA);\n\nconst fechasDetectadas = formatMultiline(info.FECHAS_TIEMPOS_RELEVANTES);\n\nconst fields = {\n ANO,\n NOMBRE_PROYECTO: cleanValue(info.NOMBRE_PROYECTO),\n CLIENTE,\n MARCA,\n MARCA_CLIENTE,\n\n PAIS: cleanValue(info.PAIS),\n RESUMEN_EJECUTIVO: sectionBetween(\n /^1\\.\\s*Resumen ejecutivo$/i,\n /^2\\.\\s*Informaci[oó]n clave del brief$/i\n ) || 'No especificado',\n\n PRODUCTO_SERVICIO: cleanValue(info.PRODUCTO_SERVICIO),\n TIPO_REQUERIMIENTO: cleanValue(info.TIPO_REQUERIMIENTO),\n TIPO_ENTREGABLE: cleanValue(info.TIPO_ENTREGABLE),\n OBJETIVO_PRINCIPAL: cleanValue(info.OBJETIVO_PRINCIPAL),\n CONTEXTO_ANTECEDENTE: cleanValue(info.CONTEXTO_ANTECEDENTE),\n AUDIENCIA_OBJETIVO: cleanValue(info.AUDIENCIA_OBJETIVO),\n MENSAJE_CLAVE: cleanValue(info.MENSAJE_CLAVE),\n\n ENTREGABLES: formatMultiline(info.ENTREGABLES),\n CANALES_FORMATOS: formatMultiline(info.CANALES_FORMATOS),\n REQUERIMIENTOS_TECNICOS: formatMultiline(info.REQUERIMIENTOS_TECNICOS),\n REFERENCIAS_VISUALES: formatMultiline(info.REFERENCIAS_VISUALES),\n\n FECHAS_TIEMPOS_RELEVANTES: buildFechasTiemposRelevantes(\n fechaEntrega,\n fechasDetectadas\n ),\n\n PRESUPUESTO: cleanValue(info.PRESUPUESTO),\n RESTRICCIONES: formatMultiline(info.RESTRICCIONES),\n\n VACIOS_DETECTADOS: bullets(\n sectionBetween(\n /^3\\.\\s*Vac[ií]os\\s+detectados$/i,\n /^4\\.\\s*Riesgos o alertas$/i\n )\n ),\n\n RIESGOS_ALERTAS: bullets(\n sectionBetween(\n /^4\\.\\s*Riesgos o alertas$/i,\n /^5\\.\\s*Recomendaciones rápidas$/i\n )\n ),\n\n RECOMENDACIONES_RAPIDAS: bullets(\n sectionBetween(\n /^5\\.\\s*Recomendaciones rápidas$/i,\n /^6\\.\\s*Nivel de completitud$/i\n )\n ),\n\n NIVEL_COMPLETITUD: completion(\n sectionBetween(\n /^6\\.\\s*Nivel de completitud$/i,\n null\n )\n ),\n\n ENLACES_REFERENCIA: support.enlacesReferenciaText || 'No se compartieron enlaces de referencia online.',\n ADJUNTOS_RECIBIDOS: support.adjuntosRecibidosText || 'No se recibieron adjuntos adicionales.'\n};\n\nconst requests = Object.entries(fields).map(([key, value]) => ({\n replaceAllText: {\n containsText: {\n text: `{{${key}}}`,\n matchCase: true,\n },\n replaceText: value,\n },\n}));\n\nreturn [\n {\n json: {\n fields,\n requests,\n },\n },\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5328,
688
],
"id": "57bef68a-7648-489c-8ffb-a7e5652a80af",
"name": "Build Docs Requests"
},
{
"parameters": {
"method": "POST",
"url": "=https://docs.googleapis.com/v1/documents/{{ $('Copy template').first().json.id }}:batchUpdate",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "googleDocsOAuth2Api",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { \"requests\": $('Build Docs Requests').first().json.requests } }}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
6784,
688
],
"id": "377a0388-d30c-4735-971e-9b4b79a07bea",
"name": "HTTP Request",
"credentials": {
"googleDocsOAuth2Api": {
"id": "2bGzUIxSb9LeUeNO",
"name": "Isaac - Google Docs"
}
}
},
{
"parameters": {
"jsCode": "const f = $json.fields || {};\n\nfunction cleanPart(value, fallback = 'No especificado') {\n const text = String(value || '').trim();\n return text || fallback;\n}\n\nfunction safePart(value, fallback = 'No especificado') {\n return cleanPart(value, fallback)\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[\\\\/:*?\"<>|]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/\\s/g, '-');\n}\n\nconst ano = safePart(f.ANO);\nconst cliente = safePart(f.CLIENTE);\nconst marca = safePart(f.MARCA);\nconst pais = safePart(f.PAIS);\nconst tipoRequerimiento = safePart(f.TIPO_REQUERIMIENTO);\n\nconst baseName = [\n ano,\n cliente,\n marca,\n pais,\n tipoRequerimiento\n].join('_');\n\nreturn [\n {\n json: {\n folderName: baseName,\n docName: baseName,\n htmlName: `${baseName}.html`,\n pdfName: `${baseName}.pdf`,\n projectName: cleanPart(f.NOMBRE_PROYECTO),\n fields: f\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5600,
688
],
"id": "d69b87b6-d228-42ba-b43a-aec0b527bf09",
"name": "Build Names"
},
{
"parameters": {
"operation": "update",
"fileId": {
"__rl": true,
"value": "={{ $('Create folder').first().json.id }}",
"mode": "id"
},
"newUpdatedFileName": "={{ $('Build Names').first().json.folderName }}",
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
5936,
688
],
"id": "c04d4432-4081-437b-b9a5-2d28387ba2d5",
"name": "Rename folder",
"credentials": {
"googleDriveOAuth2Api": {
"id": "g23xdGLZRzBGqKgH",
"name": "Isaac - Google Drive"
}
}
},
{
"parameters": {
"content": "## ENTRADA DEL FLUJO\n\n• Recibe la solicitud del frontend\n• Crea carpeta base en Google Drive\n• Esa carpeta guardará brief y adjuntos\n\nEntradas:\nvoz, imágenes, PDFs y otros archivos",
"height": 400,
"width": 304,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1504,
480
],
"id": "c1fd447e-d16c-45d3-9d9b-6a382517ae8a",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## CLASIFICACIÓN Y ENRUTAMIENTO\n\n• Identifica tipo de entrada\n• Separa: PDF, imagen, audio, texto y otros\n• Prepara cada rama para su procesamiento\n• También detecta enlaces en texto",
"height": 512,
"width": 692,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1088,
400
],
"id": "c4e13e49-10d1-4b5f-aff3-e3485aa6e333",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## GUARDADO DE ADJUNTOS\n\n• Guarda los archivos originales del usuario\n• Incluye PDFs, imágenes y otros soportes\n• No todos se analizan\n• Sí quedan preservados en la carpeta final",
"height": 320,
"width": 672,
"color": "#433D3D"
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
720,
1232
],
"id": "4dce857e-1292-43c5-b5dc-a95f33a1d9dd",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "## ANÁLISIS DE INSUMOS\n\n• PDF: se analiza con Gemini\n• Audio: se transcribe / resume\n• Imagen: se interpreta\n• Si no hay PDF, el flujo continúa\n• Cada salida se normaliza en Edit Fields",
"height": 1216,
"width": 2752,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-320,
-32
],
"id": "d96b5f33-53f1-4d3d-a146-bf3fcaf3a166",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "## CONSOLIDACIÓN DEL CONTEXTO\n\n• Une documento, audio e imagen\n• Construye un contexto único\n• Envía todo al modelo\n• El modelo genera el brief estructurado",
"height": 576,
"width": 2000,
"color": "#9E2929"
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
2704,
480
],
"id": "5ea54f56-8749-46a2-aff5-04920de7f51c",
"name": "Sticky Note4"
},
{
"parameters": {
"content": "## POSTPROCESO DEL BRIEF\n\n• Limpia la salida del modelo\n• Extrae campos del brief\n• Construye placeholders\n• Genera nombres finales\n• Renombra la carpeta creada al inicio",
"height": 352,
"width": 1252
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
4816,
496
],
"id": "1206c7e8-be96-45ce-acf7-2f21d61608a5",
"name": "Sticky Note5"
},
{
"parameters": {
"content": "## SALIDA FINAL\n\n• Copia la plantilla oficial\n• Inserta la información del brief\n• Comparte la carpeta\n• Devuelve el enlace por webhook\n• Envía el correo",
"height": 384,
"width": 2340,
"color": "#3F360C"
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
6192,
496
],
"id": "b7587368-4628-4885-a1c1-c0be75e4dba5",
"name": "Sticky Note6"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "f34e6067-5572-44ef-bac9-738212957d5c",
"leftValue": "={{ $json.pdfMissing || false }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
48,
208
],
"id": "2779ddc8-7631-417e-8800-c4a604014b4b",
"name": "If PDF missing"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "be678498-4e99-45bf-acb7-b7ade22fabb5",
"name": "fileName",
"value": "''",
"type": "string"
},
{
"id": "e9a77032-adbd-462c-bb2e-20f267496a8e",
"name": "content_summary",
"value": "No se adjuntó PDF. Continúa el flujo con audio e imagen.",
"type": "string"
},
{
"id": "bf85223a-197a-4a28-b3bf-064dc0fe8300",
"name": "type",
"value": "document",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
912,
-16
],
"id": "72c12649-c5c8-47d3-a571-cbb3f73ceae6",
"name": "PDF opcional omitido"
},
{
"parameters": {
"resource": "document",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
448,
352
],
"id": "0b5b8ccb-ebcc-4b12-984b-a336264bd4ae",
"name": "Analyze document",
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "0b8567da-0fd5-45c3-9f4e-071036d665bb",
"name": "fileName",
"value": "={{ $json.fileName || $('Switch').item.json.fileName || '' }}",
"type": "string"
},
{
"id": "7af7edfc-6c2a-4183-ac0f-f5e16e925fff",
"name": "content_summary",
"value": "={{ $json.content?.parts?.[0]?.text || $json.content_summary || '' }}",
"type": "string"
},
{
"id": "6447843f-472f-4249-ac49-bf63ed6cb04c",
"name": "type",
"value": "document",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1952,
176
],
"id": "f5280257-72a5-4721-8073-013e65b187ba",
"name": "Edit Fields documento"
},
{
"parameters": {
"jsCode": "const items = $input.all();\nreturn items;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-96,
208
],
"id": "62375717-01d5-4dc3-b106-7304ebe20c3d",
"name": "Procesar documentos"
},
{
"parameters": {
"jsCode": "const webhookItems = $('Webhook').all();\nconst out = [];\n\nlet hasPdfDocument = false;\n\nfor (const webhookItem of webhookItems) {\n const json = webhookItem.json || {};\n const body = json.body || {};\n const binary = webhookItem.binary || {};\n\n const text =\n body.text ||\n body.message ||\n body.caption ||\n body.prompt ||\n json.text ||\n json.message ||\n '';\n\n if (typeof text === 'string' && text.trim() !== '') {\n out.push({\n json: {\n type: 'text',\n text: text.trim(),\n }\n });\n }\n\n for (const key of Object.keys(binary)) {\n const file = binary[key];\n const mime = String(file.mimeType || '').toLowerCase();\n const ext = String(file.fileExtension || '').toLowerCase();\n const fileName = String(file.fileName || '').toLowerCase();\n\n let type = 'attachment_only';\n\n const isImage = mime.startsWith('image/');\n const isAudio = mime.startsWith('audio/');\n const isPdf =\n mime.includes('pdf') ||\n ext === 'pdf' ||\n fileName.endsWith('.pdf');\n\n if (isImage) {\n type = 'image';\n } else if (isAudio) {\n type = 'audio';\n } else if (isPdf) {\n type = 'pdf_document';\n hasPdfDocument = true;\n } else {\n type = 'attachment_only';\n }\n\n out.push({\n json: {\n type,\n binaryKey: key,\n mimeType: file.mimeType || '',\n fileName: file.fileName || '',\n fileExtension: file.fileExtension || '',\n fileSize: file.fileSize || '',\n },\n binary: {\n data: file\n }\n });\n }\n}\n\nif (!hasPdfDocument) {\n out.push({\n json: {\n type: 'pdf_document',\n pdfMissing: true,\n fileName: '',\n content_summary: 'No se adjuntó PDF. Continúa el flujo con audio e imagen.'\n }\n });\n}\n\nreturn out;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-800,
688
],
"id": "4ee568a2-916f-47ec-8517-a2b3ba65a9cc",
"name": "Code in JavaScript"
},
{
"parameters": {
"jsCode": "const folderId = $('Create folder').first().json.id;\nconst items = $input.all();\n\nconst output = [];\n\nfor (const item of items) {\n const binaries = item.binary || {};\n\n for (const key of Object.keys(binaries)) {\n const file = binaries[key];\n\n output.push({\n json: {\n folderId,\n fileName: file.fileName || key,\n mimeType: file.mimeType || '',\n },\n binary: {\n data: file\n }\n });\n }\n}\n\nreturn output;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
784,
1392
],
"id": "aaab3f40-a654-4289-8325-d7eab6cd73dd",
"name": "Code in JavaScript3"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nif (!items.length) {\n return [{\n json: {\n combined_content: 'No se recibió contenido para resumir.'\n }\n }];\n}\n\nconst combined = items.map((item, i) => {\n const type = item.json.type || 'other';\n const fileName = item.json.fileName || '';\n const summary = item.json.content_summary || '';\n\n return [\n `Entrada ${i + 1}`,\n `Tipo: ${type}`,\n fileName ? `Archivo: ${fileName}` : null,\n `Contenido:`,\n summary\n ].filter(Boolean).join('\\n');\n}).join('\\n\\n====================\\n\\n');\n\nreturn [\n {\n json: {\n combined_content: combined\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
3216,
752
],
"id": "cf149c50-8821-4bb9-bcd3-451fefdbdb8f",
"name": "Code in JavaScript1"
},
{
"parameters": {
"jsCode": "const raw = $('Message a model').item.json.content.parts[0].text || '';\n\nlet formatted = raw\n .replace(/\\r\\n/g, '\\n')\n .replace(/^---$/gm, '')\n .replace(/^# (.*)$/gm, '\\n$1\\n')\n .replace(/^## (.*)$/gm, '\\n$1\\n')\n .replace(/^### (.*)$/gm, '\\n$1\\n')\n .replace(/\\*\\*(.*?)\\*\\*/g, '$1')\n .replace(/^\\*\\s+/gm, '• ')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n\nformatted = `BRIEF PROFESIONAL\\n\\n${formatted}`;\n\nreturn [\n {\n json: {\n googleDocsText: formatted\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
4896,
688
],
"id": "ccd4c5dd-fdd4-4a94-bc57-fea7c40e30f9",
"name": "Code in JavaScript4"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nreturn items.map((item, index) => ({\n json: {\n ...item.json,\n _fallbackIndex: index,\n },\n binary: item.binary,\n}));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
240,
288
],
"id": "5ab6e790-bdd8-47db-ad31-ded716007eb1",
"name": "Backup document input"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nreturn items.map((item, index) => ({\n json: {\n ...item.json,\n _fallbackIndex: index,\n },\n binary: item.binary,\n}));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
576,
640
],
"id": "b38d2104-40d0-465f-8ed8-2f20d1828037",
"name": "Backup audio input"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nreturn items.map((item, index) => ({\n json: {\n ...item.json,\n _fallbackIndex: index,\n },\n binary: item.binary,\n}));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
624,
928
],
"id": "4938540d-2300-4dab-ba47-476ba38ac879",
"name": "Backup image input"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "52ff398d-d5fa-4329-8755-03578f1f3965",
"leftValue": "={{ !$json.content?.parts?.[0]?.text }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1040,
944
],
"id": "3484866a-ae5e-4d99-bfa5-b93828aa4902",
"name": "IF imagen gratis falló"
},
{
"parameters": {
"resource": "image",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
1584,
768
],
"id": "4c653881-cb9a-451a-8d26-51424ebef3bd",
"name": "Analyze an image - Pago",
"retryOnFail": false,
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"jsCode": "const failedItems = $input.all();\nconst originalItems = $('Backup image input').all();\n\nreturn failedItems.map((failedItem, index) => {\n let originalIndex = failedItem.json?._fallbackIndex;\n\n if (originalIndex === undefined && failedItem.pairedItem) {\n if (Array.isArray(failedItem.pairedItem)) {\n originalIndex = failedItem.pairedItem[0]?.item;\n } else {\n originalIndex = failedItem.pairedItem.item;\n }\n }\n\n if (originalIndex === undefined) {\n originalIndex = index;\n }\n\n const original = originalItems[originalIndex] || originalItems[index] || originalItems[0];\n\n if (!original?.binary?.data) {\n throw new Error(`No se encontró binary.data para recuperar la imagen en el índice ${originalIndex}`);\n }\n\n return {\n json: {\n ...original.json,\n fallbackUsed: true,\n fallbackReason: failedItem.json?.error || 'Error en API gratis',\n },\n binary: original.binary,\n };\n});"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1280,
768
],
"id": "11b28396-2d34-4978-b562-5190d20908bf",
"name": "Recuperar binario imagen"
},
{
"parameters": {
"resource": "audio",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
1520,
464
],
"id": "90fa8b77-56ae-4f57-b268-ba5b74a9d5f0",
"name": "Analyze audio - Pago",
"retryOnFail": false,
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "a0d47fed-e52c-4882-bd89-c7833b214111",
"leftValue": "={{ !$json.content?.parts?.[0]?.text }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
976,
656
],
"id": "6096066c-9335-4c00-8b4f-b36936ad0f42",
"name": "If audio gratis falló"
},
{
"parameters": {
"jsCode": "const failedItems = $input.all();\nconst originalItems = $('Backup audio input').all();\n\nreturn failedItems.map((failedItem, index) => {\n let originalIndex = failedItem.json?._fallbackIndex;\n\n if (originalIndex === undefined && failedItem.pairedItem) {\n if (Array.isArray(failedItem.pairedItem)) {\n originalIndex = failedItem.pairedItem[0]?.item;\n } else {\n originalIndex = failedItem.pairedItem.item;\n }\n }\n\n if (originalIndex === undefined) {\n originalIndex = index;\n }\n\n const original = originalItems[originalIndex] || originalItems[index] || originalItems[0];\n\n if (!original?.binary?.data) {\n throw new Error(`No se encontró binary.data para recuperar el audio en el índice ${originalIndex}`);\n }\n\n return {\n json: {\n ...original.json,\n fallbackUsed: true,\n fallbackReason: failedItem.json?.error || 'Error en API gratis',\n },\n binary: original.binary,\n };\n});"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1312,
464
],
"id": "e10409e3-1d67-4c8e-800b-7d9dcfd14ca6",
"name": "Recuperar binario audio"
},
{
"parameters": {
"resource": "document",
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"inputType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
1104,
160
],
"id": "49feacc4-b313-4c20-913f-05fe2246abf3",
"name": "Analyze document - Pago",
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "033b7aa0-7c40-4f6a-9056-83094b6451f0",
"leftValue": "={{ !$json.content?.parts?.[0]?.text }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
640,
352
],
"id": "92a589b2-52b9-442b-93b8-fda2ed0a00c3",
"name": "IF documento gratis falló"
},
{
"parameters": {
"jsCode": "const failedItems = $input.all();\nconst originalItems = $('Backup document input').all();\n\nreturn failedItems.map((failedItem, index) => {\n let originalIndex = failedItem.json?._fallbackIndex;\n\n if (originalIndex === undefined && failedItem.pairedItem) {\n if (Array.isArray(failedItem.pairedItem)) {\n originalIndex = failedItem.pairedItem[0]?.item;\n } else {\n originalIndex = failedItem.pairedItem.item;\n }\n }\n\n if (originalIndex === undefined) {\n originalIndex = index;\n }\n\n const original = originalItems[originalIndex] || originalItems[index] || originalItems[0];\n\n if (!original?.binary?.data) {\n throw new Error(`No se encontró binary.data para recuperar el documento en el índice ${originalIndex}`);\n }\n\n return {\n json: {\n ...original.json,\n fallbackUsed: true,\n fallbackReason: failedItem.json?.error || 'Error en API gratis',\n },\n binary: original.binary,\n };\n});"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
880,
176
],
"id": "5a3884bb-fb6f-4bfa-89bd-1b281cb7555b",
"name": "Recuperar binario documento"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-pro-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-pro-preview"
},
"messages": {
"values": [
{
"content": "=Actúa como un asistente de inteligencia artificial especializado en interpretación y estructuración de briefs creativos, estratégicos y comerciales.\n\nTu tarea es analizar todo el contenido recibido y transformarlo en un brief profesional, claro, accionable y listo para revisión interna, edición en Google Docs y representación visual en HTML.\n\nInstrucciones de trabajo:\n\n1. Captura e interpretación\n- Lee y consolida toda la información disponible.\n- Si existe texto derivado del audio, úsalo como contexto principal.\n- Si hay documentos, imágenes o referencias visuales, intégralos como soporte complementario.\n- Extrae entidades clave, objetivos, requerimientos, restricciones, entregables, fechas, audiencias, marcas, productos y cualquier dato relevante.\n- Distingue claramente entre:\n - Tipo de requerimiento = categoría general de la solicitud.\n - Tipo de entregable = pedido específico o salida concreta esperada.\n\n2. Detección de vacíos\n- Detecta campos obligatorios ausentes.\n- Identifica detalles técnicos faltantes.\n- Señala contradicciones o inconsistencias.\n- Marca información ambigua o poco clara.\n- Advierte posibles riesgos para el desarrollo creativo, estratégico o de producción.\n\n3. Enriquecimiento y estructuración\n- Reorganiza la información y conviértela en un brief profesional con redacción clara, precisa y ejecutiva.\n- Mejora la redacción sin inventar datos no proporcionados.\n- Cuando algo no esté disponible, indícalo como “No especificado” o “Pendiente de definir”.\n- Si no existe un nombre explícito para el proyecto, genera un nombre breve y profesional usando únicamente los datos reales disponibles, sin inventar información nueva.\n\n4. Reglas importantes de salida\n- No repitas información redundante.\n- No inventes datos.\n- Prioriza claridad, orden y utilidad práctica.\n- Redacta con tono profesional y ejecutivo.\n- La salida debe sentirse como un brief listo para revisión interna.\n- Debes incluir explícitamente los campos “Nombre del proyecto”, “Cliente”, “Marca”, “País”, “Tipo de requerimiento” y “Tipo de entregable”.\n- No mezcles “Tipo de requerimiento” con “Tipo de entregable”.\n- Si cliente y marca son la misma cosa, puedes repetir el valor en ambos campos.\n- Si no puedes identificar uno de los dos, usa “No especificado”.\n\n5. Genera la salida exactamente en este formato:\n\n1. Resumen ejecutivo\n- Explica de forma breve y profesional de qué trata la solicitud, cuál es el objetivo y qué se espera desarrollar.\n\n2. Información clave del brief\n- Nombre del proyecto\n- Cliente\n- Marca\n- País\n- Producto / servicio\n- Tipo de requerimiento\n- Tipo de entregable\n- Objetivo principal\n- Contexto / antecedente\n- Audiencia objetivo\n- Mensaje clave\n- Entregables solicitados\n- Canales / formatos\n- Requerimientos técnicos\n- Referencias creativas o visuales\n- Fechas o tiempos relevantes\n- Presupuesto\n- Restricciones o consideraciones especiales\n\n3. Vacíos detectados\n- Lista clara de campos faltantes, ambiguos o contradictorios.\n\n4. Riesgos o alertas\n- Señala riesgos que puedan afectar la ejecución, creatividad, tiempos, costos o claridad del proyecto.\n\n5. Recomendaciones rápidas\n- Sugiere qué información adicional conviene completar o reforzar antes de aprobar el brief final.\n\n6. Nivel de completitud\n- Indica un porcentaje estimado de completitud del brief según la información recibida.\n\nContenido recibido:\n{{$json.combined_content}}"
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
4128,
592
],
"id": "a3c326a3-4e0b-4ae6-8a0f-6eca61648af3",
"name": "Message a model - Pago",
"credentials": {
"googlePalmApi": {
"id": "jvsXYwL6IOoY2DBU",
"name": "Isaac - Gemini Api Pago"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "0ccf935c-14e8-4e1b-b7db-cb994c970733",
"leftValue": "={{ !$json.content?.parts?.[0]?.text }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
3872,
736
],
"id": "0e8aa2ac-ef86-4451-adcb-d6bda7c07146",
"name": "If"
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "1h_d4l4FYkLvyy_Zp7bEQFKpy5hx6pVj9xZ98mjMHHSU",
"mode": "list",
"cachedResultName": "CDC Brief - Base para Fulgencio",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1h_d4l4FYkLvyy_Zp7bEQFKpy5hx6pVj9xZ98mjMHHSU/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Briefs",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1h_d4l4FYkLvyy_Zp7bEQFKpy5hx6pVj9xZ98mjMHHSU/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"fecha_creacion": "={{ $json.fecha_creacion }}",
"brief_id": "={{ $json.brief_id }}",
"nombre_carpeta": "={{ $json.nombre_carpeta }}",
"enviado_por": "={{ $json.enviado_por }}",
"correo": "={{ $json.correo }}",
"link_carpeta_drive": "={{ $json.link_carpeta_drive }}",
"link_documento_brief": "={{ $json.link_documento_brief }}",
"estado": "={{ $json.estado }}",
"cantidad_audios": "={{ $json.cantidad_audios }}",
"cantidad_imagenes": "={{ $json.cantidad_imagenes }}",
"cantidad_documentos": "={{ $json.cantidad_documentos }}",
"cantidad_links": "={{ $json.cantidad_links }}",
"resumen_chatbot": "={{ $json.resumen_chatbot }}",
"texto_brief": "={{ $json.texto_brief }}",
"texto_busqueda": "={{ $json.texto_busqueda }}",
"fecha_entrega": "={{ $('Preparar fecha de entrega').first().json.deliveryDateLabel }}"
},
"matchingColumns": [],
"schema": [
{
"id": "fecha_creacion",
"displayName": "fecha_creacion",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "brief_id",
"displayName": "brief_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "nombre_carpeta",
"displayName": "nombre_carpeta",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "enviado_por",
"displayName": "enviado_por",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "correo",
"displayName": "correo",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "link_carpeta_drive",
"displayName": "link_carpeta_drive",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "link_documento_brief",
"displayName": "link_documento_brief",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "estado",
"displayName": "estado",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "cantidad_audios",
"displayName": "cantidad_audios",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "cantidad_imagenes",
"displayName": "cantidad_imagenes",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "cantidad_documentos",
"displayName": "cantidad_documentos",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "cantidad_links",
"displayName": "cantidad_links",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "resumen_chatbot",
"displayName": "resumen_chatbot",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "texto_brief",
"displayName": "texto_brief",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "texto_busqueda",
"displayName": "texto_busqueda",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "fecha_entrega",
"displayName": "fecha_entrega",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
8320,
688
],
"id": "2b332c13-0621-4eb0-a9b7-27e820963746",
"name": "Agregar fila a Sheet Fulgencio",
"retryOnFail": false,
"alwaysOutputData": true,
"credentials": {
"googleSheetsOAuth2Api": {
"id": "K0hDZh3a85MpOHCs",
"name": "Google Sheets account 2"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "function getNodeJson(nodeName) {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n}\n\nfunction getNodeBinary(nodeName) {\n try {\n return $(nodeName).first().binary || {};\n } catch (error) {\n return {};\n }\n}\n\nfunction cleanText(value) {\n return String(value || '')\n .replace(/\\r\\n/g, '\\n')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction limitText(value, maxLength = 45000) {\n const text = String(value || '');\n return text.length > maxLength ? text.slice(0, maxLength) : text;\n}\n\nfunction parseLinks(value) {\n if (!value) return [];\n\n if (Array.isArray(value)) {\n return value.filter(Boolean).map(String);\n }\n\n return String(value)\n .split(/\\n|,|\\s+/)\n .map(link => link.trim())\n .filter(link => /^https?:\\/\\//i.test(link));\n}\n\nconst webhookItem = getNodeJson('Webhook');\nconst webhookBinary = getNodeBinary('Webhook');\n\nconst body =\n webhookItem.body && Object.keys(webhookItem.body).length\n ? webhookItem.body\n : webhookItem;\n\nconst folderNode = getNodeJson('Create folder');\nconst docNode = getNodeJson('Copy template');\nconst finalTextNode = getNodeJson('Code in JavaScript4');\nconst fechaEntregaNode = getNodeJson('Preparar fecha de entrega');\n\nconst folderId = folderNode.id || '';\nconst folderName = folderNode.name || '';\nconst docId = docNode.id || '';\n\nconst folderUrl = folderId\n ? `https://drive.google.com/drive/u/0/folders/${folderId}`\n : '';\n\nconst docUrl = docId\n ? `https://docs.google.com/document/d/${docId}/edit`\n : '';\n\nconst submittedByName =\n body.submittedByName ||\n body.displayName ||\n body.name ||\n body.email ||\n 'No especificado';\n\nconst submittedByEmail =\n body.submittedByEmail ||\n body.email ||\n 'No especificado';\n\nconst fechaEntrega =\n fechaEntregaNode.deliveryDateLabel ||\n fechaEntregaNode.fechaEntregaTexto ||\n body.deliveryDateLabel ||\n body.fechaEntregaTexto ||\n 'Por confirmar';\n\nconst finalBriefText =\n finalTextNode.googleDocsText ||\n finalTextNode.text ||\n finalTextNode.content ||\n '';\n\nconst files = Object.values(webhookBinary || {});\n\nconst audioCount = files.filter(file =>\n String(file.mimeType || '').startsWith('audio/')\n).length;\n\nconst imageCount = files.filter(file =>\n String(file.mimeType || '').startsWith('image/')\n).length;\n\nconst documentCount = files.filter(file => {\n const mime = String(file.mimeType || '');\n return (\n mime.includes('pdf') ||\n mime.includes('word') ||\n mime.includes('excel') ||\n mime.includes('spreadsheet') ||\n mime.includes('presentation') ||\n mime.includes('powerpoint') ||\n mime.includes('officedocument')\n );\n}).length;\n\nconst links = [\n ...parseLinks(body.links),\n ...parseLinks(body.enlaces),\n ...parseLinks(body.referenceLinks),\n ...parseLinks(body.referencias),\n ...parseLinks(body.urls),\n];\n\nconst fechaCreacion = new Date().toISOString();\n\nconst resumenChatbot = cleanText(finalBriefText).slice(0, 1200);\n\nconst textoBusqueda = [\n `Brief CDC`,\n `Fecha: ${fechaCreacion}`,\n `Fecha límite de entrega: ${fechaEntrega}`,\n `Enviado por: ${submittedByName}`,\n `Correo: ${submittedByEmail}`,\n `Carpeta: ${folderUrl}`,\n `Documento: ${docUrl}`,\n `Nombre carpeta: ${folderName}`,\n '',\n finalBriefText,\n].join('\\n');\n\nreturn [\n {\n json: {\n fecha_creacion: fechaCreacion,\n brief_id: folderId || `${Date.now()}`,\n nombre_carpeta: folderName,\n enviado_por: submittedByName,\n correo: submittedByEmail,\n fecha_entrega: fechaEntrega,\n link_carpeta_drive: folderUrl,\n link_documento_brief: docUrl,\n estado: 'Nuevo',\n cantidad_audios: audioCount,\n cantidad_imagenes: imageCount,\n cantidad_documentos: documentCount,\n cantidad_links: links.length,\n resumen_chatbot: resumenChatbot,\n texto_brief: limitText(finalBriefText),\n texto_busqueda: limitText(textoBusqueda),\n },\n },\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
8112,
688
],
"id": "b44ed7d4-7333-44b6-b46f-d3d50a922eae",
"name": "Preparar fila para Sheet Fulgencio"
},
{
"parameters": {
"jsCode": "const inputItem = $input.first();\nconst inputJson = inputItem.json || {};\n\nconst webhookJson = $('Webhook').first().json || {};\nconst body = webhookJson.body || {};\n\nfunction formatDateEs(dateString) {\n if (!dateString) return '';\n\n const value = String(dateString).trim();\n\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n return value;\n }\n\n const [year, month, day] = value.split('-');\n const date = new Date(Number(year), Number(month) - 1, Number(day));\n\n if (Number.isNaN(date.getTime())) {\n return value;\n }\n\n return date.toLocaleDateString('es-DO', {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n });\n}\n\nconst deliveryDateRaw =\n body.deliveryDate ||\n body.fechaEntrega ||\n '';\n\nconst deliveryDateStatusRaw =\n body.deliveryDateStatus ||\n body.fechaEntregaEstado ||\n '';\n\nconst deliveryDateLabelFromFrontend =\n body.deliveryDateLabel ||\n body.fechaEntregaTexto ||\n '';\n\nconst hasDate =\n deliveryDateRaw !== undefined &&\n deliveryDateRaw !== null &&\n String(deliveryDateRaw).trim() !== '';\n\nconst normalizedStatus = String(deliveryDateStatusRaw || '').toLowerCase();\nconst normalizedLabel = String(deliveryDateLabelFromFrontend || '').toLowerCase();\n\nconst isPending =\n !hasDate &&\n (\n normalizedStatus === 'pending' ||\n normalizedStatus === 'por_confirmar' ||\n normalizedStatus === 'por confirmar' ||\n normalizedLabel === 'por confirmar'\n );\n\nconst deliveryDateLabel = isPending\n ? 'Por confirmar'\n : hasDate\n ? formatDateEs(deliveryDateRaw)\n : deliveryDateLabelFromFrontend || 'Por confirmar';\n\nreturn [\n {\n json: {\n ...inputJson,\n deliveryDateRaw,\n deliveryDateStatus: isPending ? 'pending' : 'defined',\n deliveryDateLabel,\n fechaEntregaTexto: deliveryDateLabel,\n fechaEntregaEstado: isPending ? 'por_confirmar' : 'definida',\n },\n binary: inputItem.binary,\n },\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1008,
688
],
"id": "dabcfbdd-a1db-4241-950e-6259a27586b9",
"name": "Preparar fecha de entrega"
},
{
"parameters": {
"jsCode": "const item = $input.first();\nconst json = item.json || {};\n\nconst fechaNode = $('Preparar fecha de entrega').first().json || {};\n\nconst deliveryDateLabel =\n fechaNode.deliveryDateLabel ||\n fechaNode.fechaEntregaTexto ||\n json.deliveryDateLabel ||\n json.fechaEntregaTexto ||\n 'Por confirmar';\n\nconst deliveryLine = `• Fecha límite de entrega: ${deliveryDateLabel}.`;\n\nlet googleDocsText = json.googleDocsText || '';\n\nif (!googleDocsText) {\n return [\n {\n json: {\n ...json,\n deliveryDateLabel,\n fechaEntregaTexto: deliveryDateLabel,\n },\n binary: item.binary,\n },\n ];\n}\n\n// Evita duplicar la fecha si el nodo se ejecuta más de una vez\ngoogleDocsText = googleDocsText.replace(\n /^\\s*[•-]?\\s*Fecha límite de entrega:.*$/gim,\n ''\n);\n\n// Inserta la fecha justo debajo de \"Fechas / tiempos relevantes\"\nconst fechasHeadingRegex = /(Fechas\\s*\\/\\s*tiempos\\s+relevantes\\s*:?\\s*)/i;\n\nif (fechasHeadingRegex.test(googleDocsText)) {\n googleDocsText = googleDocsText.replace(\n fechasHeadingRegex,\n `$1\\n${deliveryLine}\\n`\n );\n} else {\n // Si por alguna razón la sección no aparece, la agrega al final\n googleDocsText += `\\n\\nFechas / tiempos relevantes\\n${deliveryLine}`;\n}\n\n// Limpieza básica de saltos de línea excesivos\ngoogleDocsText = googleDocsText\n .replace(/\\n{4,}/g, '\\n\\n\\n')\n .trim();\n\nreturn [\n {\n json: {\n ...json,\n googleDocsText,\n deliveryDateLabel,\n fechaEntregaTexto: deliveryDateLabel,\n },\n binary: item.binary,\n },\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5104,
688
],
"id": "084305a2-38dd-4b3f-b900-6a3f7c26c69c",
"name": "Agregar fecha al Google Doc"
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Create folder",
"type": "main",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "Procesar documentos",
"type": "main",
"index": 0
}
],
[
{
"node": "Backup image input",
"type": "main",
"index": 0
}
],
[],
[
{
"node": "Backup audio input",
"type": "main",
"index": 0
}
]
]
},
"Analyze audio": {
"main": [
[
{
"node": "If audio gratis falló",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Code in JavaScript1",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields1": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Message a model": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Analyze an image": {
"main": [
[
{
"node": "IF imagen gratis falló",
"type": "main",
"index": 0
}
]
]
},
"Create folder": {
"main": [
[
{
"node": "Preparar fecha de entrega",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields2": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 2
}
]
]
},
"Share folder": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Respond to Webhook": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
},
"Copy template": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Build Docs Requests": {
"main": [
[
{
"node": "Build Names",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Share folder",
"type": "main",
"index": 0
}
]
]
},
"Build Names": {
"main": [
[
{
"node": "Rename folder",
"type": "main",
"index": 0
}
]
]
},
"Rename folder": {
"main": [
[
{
"node": "Copy template",
"type": "main",
"index": 0
}
]
]
},
"If PDF missing": {
"main": [
[
{
"node": "PDF opcional omitido",
"type": "main",
"index": 0
}
],
[
{
"node": "Backup document input",
"type": "main",
"index": 0
}
]
]
},
"Analyze document": {
"main": [
[
{
"node": "IF documento gratis falló",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields documento": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"PDF opcional omitido": {
"main": [
[
{
"node": "Edit Fields documento",
"type": "main",
"index": 0
}
]
]
},
"Procesar documentos": {
"main": [
[
{
"node": "If PDF missing",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Code in JavaScript3",
"type": "main",
"index": 0
},
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript3": {
"main": [
[
{
"node": "Upload file",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript1": {
"main": [
[
{
"node": "Message a model",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript4": {
"main": [
[
{
"node": "Agregar fecha al Google Doc",
"type": "main",
"index": 0
}
]
]
},
"Backup document input": {
"main": [
[
{
"node": "Analyze document",
"type": "main",
"index": 0
}
]
]
},
"Backup audio input": {
"main": [
[
{
"node": "Analyze audio",
"type": "main",
"index": 0
}
]
]
},
"Backup image input": {
"main": [
[
{
"node": "Analyze an image",
"type": "main",
"index": 0
}
]
]
},
"IF imagen gratis falló": {
"main": [
[
{
"node": "Recuperar binario imagen",
"type": "main",
"index": 0
}
],
[
{
"node": "Edit Fields2",
"type": "main",
"index": 0
}
]
]
},
"Recuperar binario imagen": {
"main": [
[
{
"node": "Analyze an image - Pago",
"type": "main",
"index": 0
}
]
]
},
"Analyze an image - Pago": {
"main": [
[
{
"node": "Edit Fields2",
"type": "main",
"index": 0
}
]
]
},
"If audio gratis falló": {
"main": [
[
{
"node": "Recuperar binario audio",
"type": "main",
"index": 0
}
],
[
{
"node": "Edit Fields1",
"type": "main",
"index": 0
}
]
]
},
"Recuperar binario audio": {
"main": [
[
{
"node": "Analyze audio - Pago",
"type": "main",
"index": 0
}
]
]
},
"Analyze audio - Pago": {
"main": [
[
{
"node": "Edit Fields1",
"type": "main",
"index": 0
}
]
]
},
"IF documento gratis falló": {
"main": [
[
{
"node": "Recuperar binario documento",
"type": "main",
"index": 0
}
],
[
{
"node": "Edit Fields documento",
"type": "main",
"index": 0
}
]
]
},
"Recuperar binario documento": {
"main": [
[
{
"node": "Analyze document - Pago",
"type": "main",
"index": 0
}
]
]
},
"Analyze document - Pago": {
"main": [
[
{
"node": "Edit Fields documento",
"type": "main",
"index": 0
}
]
]
},
"If": {
"main": [
[
{
"node": "Message a model - Pago",
"type": "main",
"index": 0
}
],
[
{
"node": "Code in JavaScript4",
"type": "main",
"index": 0
}
]
]
},
"Message a model - Pago": {
"main": [
[
{
"node": "Code in JavaScript4",
"type": "main",
"index": 0
}
]
]
},
"Send a message": {
"main": [
[
{
"node": "Preparar fila para Sheet Fulgencio",
"type": "main",
"index": 0
}
]
]
},
"Preparar fila para Sheet Fulgencio": {
"main": [
[
{
"node": "Agregar fila a Sheet Fulgencio",
"type": "main",
"index": 0
}
]
]
},
"Preparar fecha de entrega": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Agregar fecha al Google Doc": {
"main": [
[
{
"node": "Build Docs Requests",
"type": "main",
"index": 0
}
]
]
}
},
"authors": "Isaac Aracena",
"name": "Version ead282aa",
"description": "",
"autosaved": true,
"workflowPublishHistory": [
{
"createdAt": "2026-06-30T00:14:41.441Z",
"id": 2030,
"workflowId": "Z8W67i7RfrhXRDFQ",
"versionId": "ead282aa-1eee-42b4-a685-aae69f3f25e3",
"event": "activated",
"userId": "0a88c0b1-928e-4412-896e-c5d1c99b2029"
}
]
}
}