{
  "name": "OTTO - \u00c9pico 1: Resolu\u00e7\u00e3o Determin\u00edstica de Identidade",
  "nodes": [
    {
      "parameters": {
        "content": "## \ud83c\udfaf \u00c9pico 1: Resolu\u00e7\u00e3o de Identidade\n\n**Regra de Ouro**: O LLM nunca decide quem \u00e9 o lead.\nEle apenas levanta a hip\u00f3tese; o humano aperta o bot\u00e3o.\n\n**Fluxo**:\n1. Vendedor manda \u00e1udio com refer\u00eancia a lead\n2. LLM extrai entidades (profiss\u00e3o, carro, nome)\n3. SQL busca candidatos na base\n4. WhatsApp Interactive pede confirma\u00e7\u00e3o\n5. S\u00f3 continua com ID determin\u00edstico",
        "height": 300,
        "width": 420,
        "color": 4
      },
      "id": "note-docs",
      "name": "\ud83d\udccb Documenta\u00e7\u00e3o",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        40,
        40
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "otto_diario_bordo",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-audio",
      "name": "Webhook: \u00c1udio Di\u00e1rio de Bordo",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        260,
        340
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "vendedor_id",
              "name": "vendedor_id",
              "value": "={{ $json.vendedor_id }}",
              "type": "string"
            },
            {
              "id": "vendedor_whatsapp",
              "name": "vendedor_whatsapp",
              "value": "={{ $json.vendedor_whatsapp }}",
              "type": "string"
            },
            {
              "id": "transcricao",
              "name": "transcricao",
              "value": "={{ $json.transcricao }}",
              "type": "string"
            },
            {
              "id": "audio_url",
              "name": "audio_url",
              "value": "={{ $json.audio_url || '' }}",
              "type": "string"
            },
            {
              "id": "canal",
              "name": "canal",
              "value": "={{ $json.canal || 'whatsapp' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "set-vars",
      "name": "Extrair Vari\u00e1veis",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        500,
        340
      ]
    },
    {
      "parameters": {
        "model": "gpt-4o-mini",
        "messages": {
          "values": [
            {
              "content": "Voc\u00ea \u00e9 um extrator de entidades comerciais para uma concession\u00e1ria de ve\u00edculos premium.\n\nAnalise a transcri\u00e7\u00e3o do vendedor e extraia APENAS as entidades encontradas.\n\nRetorne EXATAMENTE este JSON (sem markdown, sem explica\u00e7\u00e3o):\n{\n  \"entidades\": {\n    \"nome_mencionado\": \"string ou null\",\n    \"profissao\": \"string ou null\",\n    \"veiculo\": \"string ou null\",\n    \"cor_veiculo\": \"string ou null\",\n    \"empresa\": \"string ou null\",\n    \"cidade\": \"string ou null\",\n    \"apelido\": \"string ou null\"\n  },\n  \"confianca_extracao\": 0-100\n}\n\nExemplos:\n- \"Falei com o m\u00e9dico da HRV\" \u2192 nome:null, profissao:\"m\u00e9dico\", veiculo:\"HRV\"\n- \"O Carlos da BMW branca ligou\" \u2192 nome:\"Carlos\", veiculo:\"BMW\", cor:\"branca\"\n- \"Aquele advogado de Uberl\u00e2ndia\" \u2192 profissao:\"advogado\", cidade:\"Uberl\u00e2ndia\"",
              "role": "system"
            },
            {
              "content": "={{ $json.transcricao }}",
              "role": "user"
            }
          ]
        },
        "options": {
          "temperature": 0.1,
          "maxTokens": 500
        }
      },
      "id": "llm-extrai-entidades",
      "name": "LLM: Extrair Entidades",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.4,
      "position": [
        740,
        340
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Parse entidades extra\u00eddas pelo LLM\nconst item = $input.first().json;\nconst vars = $('Extrair Vari\u00e1veis').first().json;\n\nlet entidades;\ntry {\n  const raw = typeof item.text === 'string' ? item.text : JSON.stringify(item.text);\n  entidades = JSON.parse(raw.replace(/```json?\\n?/g, '').replace(/```/g, '').trim());\n} catch (e) {\n  entidades = { entidades: {}, confianca_extracao: 0 };\n}\n\n// Montar filtros SQL din\u00e2micos\nconst filtros = [];\nconst ent = entidades.entidades || {};\n\nif (ent.nome_mencionado) filtros.push(`nome_cliente ILIKE '%${ent.nome_mencionado}%'`);\nif (ent.veiculo) filtros.push(`(veiculo_interesse ILIKE '%${ent.veiculo}%' OR metadata->>'veiculo' ILIKE '%${ent.veiculo}%')`);\nif (ent.profissao) filtros.push(`(tags ? '${ent.profissao}' OR metadata->>'profissao' ILIKE '%${ent.profissao}%')`);\nif (ent.cor_veiculo) filtros.push(`metadata->>'cor' ILIKE '%${ent.cor_veiculo}%'`);\nif (ent.empresa) filtros.push(`metadata->>'empresa' ILIKE '%${ent.empresa}%'`);\nif (ent.cidade) filtros.push(`metadata->>'cidade' ILIKE '%${ent.cidade}%'`);\nif (ent.apelido) filtros.push(`(nome_cliente ILIKE '%${ent.apelido}%' OR metadata->>'apelido' ILIKE '%${ent.apelido}%')`);\n\nconst whereClause = filtros.length > 0\n  ? `WHERE vendedor_id = '${vars.vendedor_id}' AND (${filtros.join(' OR ')})`\n  : `WHERE vendedor_id = '${vars.vendedor_id}'`;\n\nconst query = `SELECT id, nome_cliente, veiculo_interesse, estagio_atual, temperatura, tags, metadata FROM leads ${whereClause} ORDER BY ultima_interacao DESC LIMIT 5`;\n\nreturn [{ json: { ...vars, entidades: ent, confianca: entidades.confianca_extracao, query_sql: query } }];"
      },
      "id": "build-query",
      "name": "Montar Query de Busca",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        980,
        340
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "={{ $json.query_sql }}",
        "options": {}
      },
      "id": "supabase-search",
      "name": "Buscar Leads Candidatos",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1220,
        340
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Montar bot\u00f5es interativos para WhatsApp\nconst items = $input.all();\nconst vars = $('Montar Query de Busca').first().json;\nconst leads = items.map(i => i.json).filter(l => l.id);\n\nif (leads.length === 0) {\n  return [{ json: {\n    ...vars,\n    tipo_resposta: 'sem_candidatos',\n    mensagem_whatsapp: '\u26a0\ufe0f N\u00e3o encontrei nenhum lead com essas caracter\u00edsticas. Pode detalhar melhor?',\n    leads_encontrados: 0\n  }}];\n}\n\n// Montar lista de bot\u00f5es (m\u00e1ximo 3 para WhatsApp Interactive)\nconst botoes = leads.slice(0, 3).map((lead, i) => ({\n  type: 'reply',\n  reply: {\n    id: `lead_${lead.id}`,\n    title: `${i + 1}. ${(lead.nome_cliente || 'Sem nome').substring(0, 20)}`\n  }\n}));\n\n// Adicionar op\u00e7\u00e3o \"Outro lead\"\nif (leads.length > 0) {\n  botoes.push({\n    type: 'reply',\n    reply: { id: 'lead_outro', title: 'Outro lead' }\n  });\n}\n\nconst corpo = leads.slice(0, 3).map((lead, i) =>\n  `${i + 1}. *${lead.nome_cliente}* (${lead.veiculo_interesse || 'N/I'}) - ${lead.estagio_atual || 'N/I'}`\n).join('\\n');\n\nreturn [{ json: {\n  ...vars,\n  tipo_resposta: 'confirmacao',\n  leads_encontrados: leads.length,\n  leads_candidatos: leads.slice(0, 3),\n  whatsapp_payload: {\n    messaging_product: 'whatsapp',\n    to: vars.vendedor_whatsapp,\n    type: 'interactive',\n    interactive: {\n      type: 'button',\n      header: { type: 'text', text: '\ud83d\udd0d Identifica\u00e7\u00e3o de Lead' },\n      body: { text: `Encontrei ${leads.length} lead(s):\\n\\n${corpo}\\n\\nQual lead voc\u00ea est\u00e1 atualizando?` },\n      action: { buttons: botoes }\n    }\n  }\n}}];"
      },
      "id": "montar-botoes",
      "name": "Montar WhatsApp Interactive",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1460,
        340
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "has-candidates",
              "leftValue": "={{ $json.tipo_resposta }}",
              "rightValue": "confirmacao",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "if-tem-candidatos",
      "name": "Tem Candidatos?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1700,
        340
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.facebook.com/v18.0/{{ $env.WHATSAPP_PHONE_ID }}/messages",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.whatsapp_payload) }}",
        "options": {}
      },
      "id": "enviar-whatsapp-botoes",
      "name": "Enviar Bot\u00f5es WhatsApp",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1940,
        240
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.facebook.com/v18.0/{{ $env.WHATSAPP_PHONE_ID }}/messages",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"messaging_product\": \"whatsapp\",\n  \"to\": \"{{ $json.vendedor_whatsapp }}\",\n  \"type\": \"text\",\n  \"text\": { \"body\": \"{{ $json.mensagem_whatsapp }}\" }\n}",
        "options": {}
      },
      "id": "enviar-whatsapp-texto",
      "name": "Enviar Texto (Sem Candidatos)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1940,
        460
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"leads_encontrados\": {{ $('Montar WhatsApp Interactive').first().json.leads_encontrados }},\n  \"tipo_resposta\": \"{{ $('Montar WhatsApp Interactive').first().json.tipo_resposta }}\"\n}",
        "options": {}
      },
      "id": "respond-webhook",
      "name": "Responder Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        2200,
        340
      ]
    }
  ],
  "connections": {
    "Webhook: \u00c1udio Di\u00e1rio de Bordo": {
      "main": [
        [
          {
            "node": "Extrair Vari\u00e1veis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extrair Vari\u00e1veis": {
      "main": [
        [
          {
            "node": "LLM: Extrair Entidades",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM: Extrair Entidades": {
      "main": [
        [
          {
            "node": "Montar Query de Busca",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Montar Query de Busca": {
      "main": [
        [
          {
            "node": "Buscar Leads Candidatos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar Leads Candidatos": {
      "main": [
        [
          {
            "node": "Montar WhatsApp Interactive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Montar WhatsApp Interactive": {
      "main": [
        [
          {
            "node": "Tem Candidatos?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tem Candidatos?": {
      "main": [
        [
          {
            "node": "Enviar Bot\u00f5es WhatsApp",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Enviar Texto (Sem Candidatos)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enviar Bot\u00f5es WhatsApp": {
      "main": [
        [
          {
            "node": "Responder Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enviar Texto (Sem Candidatos)": {
      "main": [
        [
          {
            "node": "Responder Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "id": "otto-1",
      "name": "OTTO"
    },
    {
      "id": "otto-2",
      "name": "Identidade"
    },
    {
      "id": "otto-3",
      "name": "\u00c9pico 1"
    }
  ],
  "triggerCount": 1,
  "updatedAt": "2026-02-26T12:00:00.000Z",
  "versionId": "1"
}