{
  "name": "OTTO - \u00c9pico 2: Funil de Hip\u00f3tese (IA Sugere, Sistema Julga)",
  "nodes": [
    {
      "parameters": {
        "content": "## \ud83e\udde0 \u00c9pico 2: Funil de Hip\u00f3tese\n\n**Regra**: A IA interpreta a sem\u00e2ntica, mas uma m\u00e1quina de estados aprova a transi\u00e7\u00e3o.\n\n**Fluxo**:\n1. Recebe lead_id confirmado (do \u00c9pico 1) + transcri\u00e7\u00e3o\n2. LLM prop\u00f5e hip\u00f3tese de evento\n3. Code Node valida transi\u00e7\u00e3o cronol\u00f3gica\n4. Se v\u00e1lido \u2192 salva evento + incrementa version\n5. Se inv\u00e1lido \u2192 notifica vendedor da regra",
        "height": 280,
        "width": 420,
        "color": 5
      },
      "id": "note-docs",
      "name": "\ud83d\udccb Documenta\u00e7\u00e3o",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        40,
        40
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "otto_hipotese_comercial",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-hipotese",
      "name": "Webhook: Hip\u00f3tese Comercial",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        260,
        340
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "lead_id",
              "name": "lead_id",
              "value": "={{ $json.lead_id }}",
              "type": "string"
            },
            {
              "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": "estagio_atual",
              "name": "estagio_atual",
              "value": "={{ $json.estagio_atual }}",
              "type": "string"
            },
            {
              "id": "state_version",
              "name": "state_version",
              "value": "={{ $json.state_version }}",
              "type": "number"
            },
            {
              "id": "temperatura",
              "name": "temperatura",
              "value": "={{ $json.temperatura || 'morno' }}",
              "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 analista comercial de concession\u00e1ria de ve\u00edculos premium.\n\nAnalise a transcri\u00e7\u00e3o do vendedor e proponha UMA hip\u00f3tese de evento comercial.\n\nEst\u00e1gios do funil (em ordem):\nCONTATO_INICIAL \u2192 QUALIFICACAO \u2192 VISITA_AGENDADA \u2192 VISITA_REALIZADA \u2192 PROPOSTA \u2192 NEGOCIACAO \u2192 APROVACAO_FINANCEIRA \u2192 FECHAMENTO \u2192 POS_VENDA\n\nTipos de evento poss\u00edveis:\n- AVANCO_ETAPA: progresso real na negocia\u00e7\u00e3o\n- OBJECAO_REGISTRADA: cliente levantou obje\u00e7\u00e3o (pre\u00e7o, prazo, financiamento)\n- PROPOSTA_ENVIADA: proposta formal foi apresentada\n- PROPOSTA_ACEITA / PROPOSTA_RECUSADA\n- TESTE_DRIVE: cliente fez/agendou test drive\n- FINANCIAMENTO_APROVADO / FINANCIAMENTO_RECUSADO\n- CONTRATO_ASSINADO\n- INTERACAO_INVALIDA: sauda\u00e7\u00f5es, respostas evasivas, sem avan\u00e7o real\n- LEAD_PERDIDO: cliente desistiu definitivamente\n\nEst\u00e1gio atual do lead: {{ $json.estagio_atual }}\n\nRetorne EXATAMENTE este JSON:\n{\n  \"hipotese_evento\": \"TIPO_DO_EVENTO\",\n  \"novo_estagio\": \"ESTAGIO_SUGERIDO ou null se n\u00e3o muda\",\n  \"nivel_confianca\": 0-100,\n  \"resumo_comercial\": \"Frase curta do que aconteceu\",\n  \"objecao_detectada\": \"string ou null\",\n  \"razao\": \"Por que voc\u00ea classificou assim\"\n}",
              "role": "system"
            },
            {
              "content": "={{ $json.transcricao }}",
              "role": "user"
            }
          ]
        },
        "options": {
          "temperature": 0.2,
          "maxTokens": 600
        }
      },
      "id": "llm-hipotese",
      "name": "LLM: Agente de Hip\u00f3tese",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.4,
      "position": [
        740,
        340
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// VALIDADOR DE TRANSI\u00c7\u00c3O (M\u00e1quina de Estados Determin\u00edstica)\nconst vars = $('Extrair Vari\u00e1veis').first().json;\nconst llmRaw = $input.first().json;\n\nlet hipotese;\ntry {\n  const raw = typeof llmRaw.text === 'string' ? llmRaw.text : JSON.stringify(llmRaw.text);\n  hipotese = JSON.parse(raw.replace(/```json?\\n?/g, '').replace(/```/g, '').trim());\n} catch (e) {\n  return [{ json: { ...vars, resultado: 'ERRO_PARSE', mensagem: 'N\u00e3o consegui interpretar o \u00e1udio. Pode repetir?' } }];\n}\n\n// Mapa de transi\u00e7\u00f5es v\u00e1lidas\nconst TRANSICOES = {\n  'CONTATO_INICIAL': ['QUALIFICACAO'],\n  'QUALIFICACAO': ['VISITA_AGENDADA'],\n  'VISITA_AGENDADA': ['VISITA_REALIZADA'],\n  'VISITA_REALIZADA': ['PROPOSTA'],\n  'PROPOSTA': ['NEGOCIACAO', 'QUALIFICACAO'],\n  'NEGOCIACAO': ['APROVACAO_FINANCEIRA', 'PROPOSTA'],\n  'APROVACAO_FINANCEIRA': ['FECHAMENTO', 'NEGOCIACAO'],\n  'FECHAMENTO': ['POS_VENDA']\n};\n\nconst evento = hipotese.hipotese_evento;\nconst novoEstagio = hipotese.novo_estagio;\n\n// INTERA\u00c7\u00c3O INV\u00c1LIDA - cron\u00f4metro n\u00e3o recome\u00e7a\nif (evento === 'INTERACAO_INVALIDA') {\n  return [{ json: {\n    ...vars,\n    hipotese,\n    resultado: 'INTERACAO_INVALIDA',\n    evento_salvar: 'INTERACAO_INVALIDA',\n    mensagem: `\ud83d\udcdd Anotado. Mas lembre-se: o rel\u00f3gio da ${vars.estagio_atual === 'PROPOSTA' ? 'proposta' : 'negocia\u00e7\u00e3o'} dele continua rodando.`,\n    cronometro_reseta: false\n  }}];\n}\n\n// Validar transi\u00e7\u00e3o cronol\u00f3gica\nif (novoEstagio && novoEstagio !== vars.estagio_atual) {\n  const permitidas = TRANSICOES[vars.estagio_atual] || [];\n  if (!permitidas.includes(novoEstagio)) {\n    return [{ json: {\n      ...vars,\n      hipotese,\n      resultado: 'TRANSICAO_INVALIDA',\n      evento_salvar: null,\n      mensagem: `\u26a0\ufe0f N\u00e3o posso mover de ${vars.estagio_atual} para ${novoEstagio}. O lead precisa passar pelas etapas intermedi\u00e1rias.`,\n      cronometro_reseta: false\n    }}];\n  }\n}\n\n// EVENTO V\u00c1LIDO\nreturn [{ json: {\n  ...vars,\n  hipotese,\n  resultado: 'EVENTO_VALIDO',\n  evento_salvar: evento,\n  novo_estagio: novoEstagio || vars.estagio_atual,\n  mensagem: `\u2705 ${hipotese.resumo_comercial}\\n\\n\ud83d\udcca Confian\u00e7a: ${hipotese.nivel_confianca}%${hipotese.objecao_detectada ? '\\n\u26a0\ufe0f Obje\u00e7\u00e3o: ' + hipotese.objecao_detectada : ''}`,\n  cronometro_reseta: true\n}}];"
      },
      "id": "validador-transicao",
      "name": "Validador de Transi\u00e7\u00e3o",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        980,
        340
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "is-valid",
              "leftValue": "={{ $json.resultado }}",
              "rightValue": "EVENTO_VALIDO",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "if-evento-valido",
      "name": "Evento V\u00e1lido?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1220,
        340
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "=INSERT INTO eventos_comerciais (lead_id, vendedor_id, tipo_evento, estagio_anterior, estagio_novo, evidencia_ia, nivel_confianca, state_version_at) VALUES ('{{ $json.lead_id }}', '{{ $json.vendedor_id }}', '{{ $json.evento_salvar }}', '{{ $json.estagio_atual }}', '{{ $json.novo_estagio }}', '{{ JSON.stringify($json.hipotese).replace(/'/g, \"''\") }}'::jsonb, {{ $json.hipotese.nivel_confianca }}, {{ $json.state_version }}); UPDATE leads SET estagio_funil = '{{ $json.novo_estagio }}', estagio_atual = '{{ $json.novo_estagio }}', ultima_interacao = NOW() WHERE id = '{{ $json.lead_id }}';",
        "options": {}
      },
      "id": "salvar-evento",
      "name": "Salvar Evento + Atualizar Lead",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1460,
        240
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "=INSERT INTO eventos_comerciais (lead_id, vendedor_id, tipo_evento, evidencia_ia, nivel_confianca, state_version_at) VALUES ('{{ $('Extrair Vari\u00e1veis').first().json.lead_id }}', '{{ $('Extrair Vari\u00e1veis').first().json.vendedor_id }}', '{{ $json.evento_salvar || 'INTERACAO_INVALIDA' }}', '{{ JSON.stringify($json.hipotese || {}).replace(/'/g, \"''\") }}'::jsonb, {{ ($json.hipotese || {}).nivel_confianca || 0 }}, {{ $('Extrair Vari\u00e1veis').first().json.state_version }});",
        "options": {}
      },
      "id": "salvar-interacao-invalida",
      "name": "Registrar Intera\u00e7\u00e3o (Sem Avan\u00e7o)",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1460,
        460
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.facebook.com/v18.0/{{ $env.WHATSAPP_PHONE_ID }}/messages",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"messaging_product\": \"whatsapp\",\n  \"to\": \"{{ $('Extrair Vari\u00e1veis').first().json.vendedor_whatsapp }}\",\n  \"type\": \"text\",\n  \"text\": { \"body\": \"{{ $('Validador de Transi\u00e7\u00e3o').first().json.mensagem }}\" }\n}",
        "options": {}
      },
      "id": "notificar-vendedor",
      "name": "Notificar Vendedor",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1700,
        340
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"resultado\": \"{{ $('Validador de Transi\u00e7\u00e3o').first().json.resultado }}\",\n  \"evento\": \"{{ $('Validador de Transi\u00e7\u00e3o').first().json.evento_salvar }}\",\n  \"cronometro_reseta\": {{ $('Validador de Transi\u00e7\u00e3o').first().json.cronometro_reseta }}\n}",
        "options": {}
      },
      "id": "respond-webhook",
      "name": "Responder Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1940,
        340
      ]
    }
  ],
  "connections": {
    "Webhook: Hip\u00f3tese Comercial": {
      "main": [
        [
          {
            "node": "Extrair Vari\u00e1veis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extrair Vari\u00e1veis": {
      "main": [
        [
          {
            "node": "LLM: Agente de Hip\u00f3tese",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM: Agente de Hip\u00f3tese": {
      "main": [
        [
          {
            "node": "Validador de Transi\u00e7\u00e3o",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validador de Transi\u00e7\u00e3o": {
      "main": [
        [
          {
            "node": "Evento V\u00e1lido?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evento V\u00e1lido?": {
      "main": [
        [
          {
            "node": "Salvar Evento + Atualizar Lead",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Registrar Intera\u00e7\u00e3o (Sem Avan\u00e7o)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Salvar Evento + Atualizar Lead": {
      "main": [
        [
          {
            "node": "Notificar Vendedor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Registrar Intera\u00e7\u00e3o (Sem Avan\u00e7o)": {
      "main": [
        [
          {
            "node": "Notificar Vendedor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notificar Vendedor": {
      "main": [
        [
          {
            "node": "Responder Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "id": "otto-1",
      "name": "OTTO"
    },
    {
      "id": "otto-4",
      "name": "Hip\u00f3tese"
    },
    {
      "id": "otto-5",
      "name": "\u00c9pico 2"
    }
  ],
  "triggerCount": 1,
  "updatedAt": "2026-02-26T12:00:00.000Z",
  "versionId": "1"
}