{
  "name": "Logixb2b — Acelerar ciclo de cierre con IA",
  "meta": {
    "templateCredsSetupCompleted": false,
    "description": "Schedule diario → GET Leads estancados → LLM follow-up → tarea CRM → Slack. Sin auto-envío al cliente. Incluye Asistente IA (OpenAI) configurable."
  },
  "settings": {
    "executionOrder": "v1",
    "timezone": "Europe/Madrid"
  },
  "active": false,
  "pinData": {},
  "tags": [
    {
      "name": "Logixb2b"
    },
    {
      "name": "recursos"
    },
    {
      "name": "ia"
    },
    {
      "name": "pipeline"
    }
  ],
  "nodes": [
    {
      "id": "sticky-instrucciones-logix",
      "name": "INSTRUCCIONES (conectar + VPS)",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -480,
        40
      ],
      "parameters": {
        "content": "## INSTRUCCIONES — Acelerar ciclo de cierre con IA\n\n**Recurso:** `/recursos/acelerar-ciclo-cierre-ia`\n**Guía:** /ia-reducir-ciclo-cierre-ventas-b2b\n**Zona:** Europe/Madrid · `active: false` al importar (tú lo activas).\n\n### 1) Importar\n1. n8n → ⋯ → **Import from File** → este JSON.\n2. El flujo llega **inactive**. No pegues secretos en el lienzo ni en el archivo.\n\n### 2) Credenciales y configuración por nodo\n- **Diario 09:00 Madrid** (`schedule`): sin credencial. Revisa expresión cron / intervalo y zona **Europe/Madrid** (settings del workflow). Solo dispara si n8n está **Active** 24/7.\n- **Config ciclo** (`Set` Config): **no lleva credencial**. Aquí van URLs/placeholders (`REEMPLAZA_*`, `example.com`, `TU-CRM`). Sustituye todos antes de Active.\n- **Obtener Leads estancados del CRM** (`HTTP Request` CRM): Credentials → **Header Auth** / OAuth del CRM. Sustituye `crm*Url` en el nodo **Config**. Prueba con un Lead de sandbox.\n- **Normalizar Lead (1º ítem)** (`Set`): sin credencial. Revisa expresiones `={{ }}` y campos de salida.\n- **¿daysSinceTouch > N?** (`IF`): sin credencial. Comprueba la condición (email, umbral, over_limit, etc.) con datos reales de prueba.\n- **Parsear borrador** (`Set`): sin credencial. Revisa expresiones `={{ }}` y campos de salida.\n- **POST tarea CRM** (`HTTP Request` CRM): Credentials → **Header Auth** / OAuth del CRM. Sustituye `crm*Url` en el nodo **Config**. Prueba con un Lead de sandbox.\n- **Slack Lead estancado** (`HTTP Request` → Slack/Teams): crea Incoming Webhook en Slack (o Adaptive Card URL en Teams). Pega la URL en **Config** o en este nodo. Auth: none (la URL ya es secreta).\n- **Preparar prompt IA** (`Set`): sin credencial. Revisa expresiones `={{ }}` y campos de salida.\n- **Asistente IA** (`AI Agent`): no lleva API key propia; debe estar cableado al nodo **Modelo OpenAI**. Revisa el prompt del sistema y que la salida pase por revisión humana (HITL) antes de escribir al CRM.\n- **Modelo OpenAI** (`OpenAI / LLM`): Credentials → **OpenAI API** (o compatible: base URL + API key). Nunca pegues la key en el JSON. El Asistente IA depende de este nodo.\n- **Aplicar salida IA** (`Set`): sin credencial. Revisa expresiones `={{ }}` y campos de salida.\n\n### 3) Orden recomendado de puesta en marcha\n1. Rellena el nodo **Config** (URLs reales, umbrales, webhooks de Slack).\n2. Crea Credentials en n8n (candado de cada nodo HTTP / Sheets / OpenAI) y **enlázalas**.\n3. Ejecuta **una vez** (Manual / Test URL del Webhook / \"Execute workflow\").\n4. Verifica CRM + Slack/Sheets (y que la IA no escriba en producción sin HITL).\n5. Solo entonces: interruptor **Active**.\n\n### 4) Producción 24/7 en VPS (UE)\nImportar en localhost no basta: webhooks y cron necesitan n8n encendido.\n\n1. VPS en UE (Hetzner/OVH/IONOS…) + Docker Compose + **Postgres** + HTTPS (Caddy/Nginx).\n2. Variables mínimas: `GENERIC_TIMEZONE=Europe/Madrid`, `WEBHOOK_URL=https://n8n.tudominio.com/`, `N8N_ENCRYPTION_KEY`, `N8N_HOST` / `N8N_PROTOCOL=https`.\n3. Firewall: solo 80/443 (+ SSH). Backups del volumen Postgres + export de workflows.\n4. Importa este JSON en la instancia de producción, vuelve a crear Credentials (no se copian solas), actualiza URLs del Config al dominio real.\n5. Pon **Active**. Prueba el Webhook de producción o espera el próximo cron.\n6. Alternativa: **n8n Cloud** (mismo JSON; Credentials en la UI Cloud).\n\n### 5) RGPD + EU AI Act (resumen)\nSi hay nodos IA sois **deployers** (Reglamento UE 2024/1689). Art. 4 alfabetización. **HITL** antes de outreach, scores masivos o pausas de cuenta. Minimiza PII en Slack/logs. Informativo — no es asesoramiento legal. /uso-responsable-de-ia-y-transparencia\n",
        "height": 1500,
        "width": 580,
        "color": 4
      }
    },
    {
      "id": "sched-daily",
      "name": "Diario 09:00 Madrid",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 * * *"
            }
          ]
        }
      }
    },
    {
      "id": "set-config",
      "name": "Config ciclo",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        300,
        300
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "c1",
              "name": "crmStalledUrl",
              "value": "https://TU-CRM.ejemplo/api/leads?status=open&stalled=1",
              "type": "string"
            },
            {
              "id": "c2",
              "name": "daysThreshold",
              "value": 7,
              "type": "number"
            },
            {
              "id": "c3",
              "name": "llmUrl",
              "value": "https://api.openai.com/v1/chat/completions",
              "type": "string"
            },
            {
              "id": "c4",
              "name": "apiKey",
              "value": "sustituye-con-tu-clave-del-modelo-ia",
              "type": "string"
            },
            {
              "id": "c5",
              "name": "model",
              "value": "gpt-4o-mini",
              "type": "string"
            },
            {
              "id": "c6",
              "name": "crmTaskUrl",
              "value": "https://TU-CRM.ejemplo/api/tasks",
              "type": "string"
            },
            {
              "id": "c7",
              "name": "slackWebhookUrl",
              "value": "https://example.com/aviso-equipo-slack",
              "type": "string"
            }
          ]
        }
      }
    },
    {
      "id": "http-crm-get",
      "name": "Obtener Leads estancados del CRM",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        600,
        300
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $json.crmStalledUrl }}",
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "set-deal",
      "name": "Normalizar Lead (1º ítem)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        900,
        300
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "d1",
              "name": "dealId",
              "value": "={{ ($json.deals && $json.deals[0] && $json.deals[0].id) || $json.id || $json.dealId || 'DEAL-DEMO' }}",
              "type": "string"
            },
            {
              "id": "d2",
              "name": "dealName",
              "value": "={{ ($json.deals && $json.deals[0] && ($json.deals[0].name || $json.deals[0].title)) || $json.name || $json.title || 'Oportunidad demo' }}",
              "type": "string"
            },
            {
              "id": "d3",
              "name": "owner",
              "value": "={{ ($json.deals && $json.deals[0] && $json.deals[0].owner) || $json.owner || '' }}",
              "type": "string"
            },
            {
              "id": "d4",
              "name": "daysSinceTouch",
              "value": "={{ Number(($json.deals && $json.deals[0] && $json.deals[0].daysSinceTouch) || $json.daysSinceTouch || 0) }}",
              "type": "number"
            },
            {
              "id": "d5",
              "name": "lastNote",
              "value": "={{ ($json.deals && $json.deals[0] && $json.deals[0].lastNote) || $json.lastNote || '' }}",
              "type": "string"
            },
            {
              "id": "d6",
              "name": "daysThreshold",
              "value": "={{ $('Config ciclo').item.json.daysThreshold }}",
              "type": "number"
            },
            {
              "id": "d7",
              "name": "llmUrl",
              "value": "={{ $('Config ciclo').item.json.llmUrl }}",
              "type": "string"
            },
            {
              "id": "d8",
              "name": "apiKey",
              "value": "={{ $('Config ciclo').item.json.apiKey }}",
              "type": "string"
            },
            {
              "id": "d9",
              "name": "model",
              "value": "={{ $('Config ciclo').item.json.model }}",
              "type": "string"
            },
            {
              "id": "d10",
              "name": "crmTaskUrl",
              "value": "={{ $('Config ciclo').item.json.crmTaskUrl }}",
              "type": "string"
            },
            {
              "id": "d11",
              "name": "slackWebhookUrl",
              "value": "={{ $('Config ciclo').item.json.slackWebhookUrl }}",
              "type": "string"
            },
            {
              "id": "d12",
              "name": "isStalled",
              "value": "={{ Number(($json.deals && $json.deals[0] && $json.deals[0].daysSinceTouch) || $json.daysSinceTouch || 0) >= Number($('Config ciclo').item.json.daysThreshold) }}",
              "type": "boolean"
            }
          ]
        }
      }
    },
    {
      "id": "if-stalled",
      "name": "¿daysSinceTouch > N?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1200,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "if-stalled-cond",
              "leftValue": "={{ $json.isStalled }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "set-draft",
      "name": "Parsear borrador",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2400,
        40
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "p1",
              "name": "asunto",
              "value": "={{ (() => { try { return JSON.parse($json.aiOutput).asunto; } catch(e) { return 'Seguimiento oportunidad'; } })() }}",
              "type": "string"
            },
            {
              "id": "p2",
              "name": "borrador",
              "value": "={{ (() => { try { return JSON.parse($json.aiOutput).borrador; } catch(e) { return ''; } })() }}",
              "type": "string"
            },
            {
              "id": "p3",
              "name": "siguientePaso",
              "value": "={{ (() => { try { return JSON.parse($json.aiOutput).siguientePaso; } catch(e) { return ''; } })() }}",
              "type": "string"
            },
            {
              "id": "p4",
              "name": "dealId",
              "value": "={{ $('Normalizar Lead (1º ítem)').item.json.dealId }}",
              "type": "string"
            },
            {
              "id": "p5",
              "name": "dealName",
              "value": "={{ $('Normalizar Lead (1º ítem)').item.json.dealName }}",
              "type": "string"
            },
            {
              "id": "p6",
              "name": "crmTaskUrl",
              "value": "={{ $('Normalizar Lead (1º ítem)').item.json.crmTaskUrl }}",
              "type": "string"
            },
            {
              "id": "p7",
              "name": "slackWebhookUrl",
              "value": "={{ $('Normalizar Lead (1º ítem)').item.json.slackWebhookUrl }}",
              "type": "string"
            },
            {
              "id": "p8",
              "name": "daysSinceTouch",
              "value": "={{ $('Normalizar Lead (1º ítem)').item.json.daysSinceTouch }}",
              "type": "number"
            }
          ]
        }
      }
    },
    {
      "id": "http-task",
      "name": "POST tarea CRM",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2700,
        40
      ],
      "parameters": {
        "method": "POST",
        "url": "={{ $json.crmTaskUrl }}",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ dealId: $json.dealId, title: '[IA] Seguimiento: ' + $json.asunto, body: $json.borrador + '\\n\\nSiguiente paso: ' + $json.siguientePaso, needs_human_review: true }) }}",
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "http-slack",
      "name": "Slack Lead estancado",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3000,
        40
      ],
      "parameters": {
        "method": "POST",
        "url": "={{ $('Parsear borrador').item.json.slackWebhookUrl }}",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ text: ':hourglass_flowing_sand: *Lead estancado* — ' + $('Parsear borrador').item.json.dealName + '\\nSin contacto: ' + $('Parsear borrador').item.json.daysSinceTouch + ' días\\nBorrador listo en tarea CRM (revisar antes de enviar).' }) }}",
        "options": {
          "timeout": 10000
        }
      }
    },
    {
      "id": "noop-ok",
      "name": "Sin estancamiento",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1500,
        560
      ],
      "parameters": {}
    },
    {
      "id": "ai-prep-acelerar-cic",
      "name": "Preparar prompt IA",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1500,
        40
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "includeOtherFields": true,
        "assignments": {
          "assignments": [
            {
              "id": "p1",
              "name": "aiPrompt",
              "value": "={{ \"Eres un closer coach. Para el Lead estancado, redacta un follow-up corto y el next step en CRM. Español.\\n\\nDeal:\\n\" + JSON.stringify($json) + \"\" }}",
              "type": "string"
            }
          ]
        }
      }
    },
    {
      "id": "ai-agent-acelerar-cic",
      "name": "Asistente IA",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.7,
      "position": [
        1800,
        40
      ],
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.aiPrompt }}",
        "options": {}
      }
    },
    {
      "id": "ai-model-acelerar-cic",
      "name": "Modelo OpenAI",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        1800,
        320
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {}
      }
    },
    {
      "id": "ai-apply-acelerar-cic",
      "name": "Aplicar salida IA",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2100,
        40
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "includeOtherFields": true,
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "aiOutput",
              "value": "={{ $json.output || $json.text || $json.message || JSON.stringify($json) }}",
              "type": "string"
            },
            {
              "id": "a2",
              "name": "slackText",
              "value": "={{ ($json.output || $json.text || '').toString() || 'Revisa el resultado del Asistente IA.' }}",
              "type": "string"
            }
          ]
        }
      }
    }
  ],
  "connections": {
    "Diario 09:00 Madrid": {
      "main": [
        [
          {
            "node": "Config ciclo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config ciclo": {
      "main": [
        [
          {
            "node": "Obtener Leads estancados del CRM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Obtener Leads estancados del CRM": {
      "main": [
        [
          {
            "node": "Normalizar Lead (1º ítem)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalizar Lead (1º ítem)": {
      "main": [
        [
          {
            "node": "¿daysSinceTouch > N?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "¿daysSinceTouch > N?": {
      "main": [
        [
          {
            "node": "Preparar prompt IA",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sin estancamiento",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parsear borrador": {
      "main": [
        [
          {
            "node": "POST tarea CRM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "POST tarea CRM": {
      "main": [
        [
          {
            "node": "Slack Lead estancado",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar prompt IA": {
      "main": [
        [
          {
            "node": "Asistente IA",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Asistente IA": {
      "main": [
        [
          {
            "node": "Aplicar salida IA",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Modelo OpenAI": {
      "ai_languageModel": [
        [
          {
            "node": "Asistente IA",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Aplicar salida IA": {
      "main": [
        [
          {
            "node": "Parsear borrador",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
