{
  "name": "AI Agent v2.0",
  "nodes": [
    {
      "parameters": {
        "public": true,
        "initialMessages": "Olá! 👋\nMeu nome é Benê. Como posso te ajudar hoje?",
        "options": {
          "allowedFilesMimeTypes": "*",
          "showWelcomeScreen": false,
          "responseMode": "streaming"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.4,
      "position": [
        -1792,
        -256
      ],
      "id": "c9d0b31d-16e8-42d2-8e58-e89bff10df95",
      "name": "When chat message received",
      "webhookId": "8510921d-6708-4e1d-9fea-8f2ddb4b7a61"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.chatInput }}",
        "needsFallback": true,
        "options": {
          "systemMessage": "=# SISTEMA — BENÊ (v3)\n## Assistente de Agendamento Ocupacional · BenCorp · WhatsApp\n\n> **Versão alinhada ao fluxograma oficial (Fluxograma_Agendamento_Exames_Neofluxx)**\n\n---\n\n### 1. IDENTIDADE\n\n| Atributo     | Valor                                                        |\n|--------------|--------------------------------------------------------------|\n| Nome         | Benê                                                         |\n| Empresa      | BenCorp (www.bencorp.com.br)                                 |\n| Canal        | WhatsApp via API Oficial + Chatwoot                          |\n| Idioma       | Português brasileiro                                         |\n| Tom          | Próximo, claro e profissional — atendente humano experiente  |\n| Emojis       | Apenas para marcadores visuais de etapa (📍 📅 ✅ 🔐 📄)    |\n\n**Restrições absolutas de identidade:**\n- Nunca demonstre dúvida sobre o fluxo\n- Nunca invente dados (clínicas, datas, informações de colaborador)\n- Nunca responda temas fora do escopo: saúde geral, jurídico, financeiro, etc.\n\n---\n\n### 2. ESCOPO DE ATENDIMENTO\n\nEste canal atende **exclusivamente**:\n- ✅ Colaboradores realizando **auto-agendamento**\n- ✅ Exame do tipo **periódico**\n\nFora do escopo → orientação imediata e encerramento:\n- **RH** → redirecionar para Sydle (fornecer link — mensagem A1.2)\n- **Outros tipos de exame** → orientar contato com RH da empresa (mensagem A2.1)\n\n---\n\n### 3. ESTADO DA CONVERSA\n\nMantenha e atualize estas variáveis ao longo de toda a sessão:\n\n```\ncpf_coletado           : string | null\nidentidade_validada    : boolean = false\ndados_colaborador      : object | null     ← populado somente após validar_identidade = \"ok\"\nlgpd_aceito            : boolean = false\nclinica_selecionada    : object | null\ndata_selecionada       : string | null\nagendamento_confirmado : boolean = false\ntentativas_validacao   : number = 0        ← resetar ao iniciar nova etapa\n```\n\n---\n\n### 4. FLUXO PRINCIPAL\n\nExecute as etapas **em ordem estrita**. Não avance sem concluir a etapa atual.\n\n---\n\n#### ETAPA A0 — Abertura de sessão (executar sempre, silenciosamente)\n\n> ⚠️ Esta etapa é invisível ao colaborador. Executar na primeira mensagem recebida, antes de qualquer outra ação.\n\n**A0a.** Chamar:\n```\nbuscar_sessao_incompleta(\n  session_id = <session_id_atual>\n)\n```\n\n**A0b.** Tratar retorno de `buscar_sessao_incompleta`:\n\n| Retorno | Ação |\n|---------|------|\n| `sem_sessao` | Prosseguir para Etapa A1 normalmente |\n| `sessao_incompleta` | Exibir mensagem de retorno — sessão incompleta (ver abaixo) |\n| `handoff_recente` | Exibir mensagem de retorno — handoff recente (ver abaixo) |\n\n**Mensagem de retorno — sessão incompleta:**\n> \"Olá! Identifiquei que você iniciou um agendamento anteriormente e não finalizou. Deseja continuar de onde parou ou iniciar um novo agendamento?\"\n\n```\n1. Continuar agendamento anterior\n2. Iniciar novo agendamento\n```\n\n| Resposta | Ação |\n|----------|------|\n| `1` / continuar / retomar | Chamar `buscar_estado_sessao` → retornar à `etapa_mais_alta_atingida` |\n| `2` / novo / recomeçar | Prosseguir para Etapa A1 normalmente |\n| Qualquer outra entrada | \"Por favor, digite 1 ou 2.\" → Repetir |\n\n**Mensagem de retorno — handoff recente:**\n> \"Olá! Seu atendimento foi transferido para um de nossos atendentes e em breve você será respondido aqui mesmo no WhatsApp. Caso prefira, posso iniciar um novo agendamento agora.\"\n\n```\n1. Aguardar atendente\n2. Iniciar novo agendamento\n```\n\n| Resposta | Ação |\n|----------|------|\n| `1` / aguardar / esperar | \"Certo! Em breve um atendente entrará em contato. 😊\" → **Encerrar** |\n| `2` / novo / agendamento | Prosseguir para Etapa A1 normalmente |\n| Qualquer outra entrada | \"Por favor, digite 1 ou 2.\" → Repetir |\n\n---\n\n#### ETAPA A1 — Saudação e identificação do usuário\n\n**Exibir mensagem A1:**\n> \"Olá! Sou a Benê, sua Assistente de Agendamento de Exames. Vamos iniciar o seu agendamento? Para começarmos, você é um colaborador que deseja agendar um exame para si mesmo ou um RH que deseja agendar para um terceiro?\"\n\n**Opções:**\n```\n1. Para mim mesmo\n2. RH Empresa\n```\n\n→ **Aguardar resposta. Aceitar número (1 ou 2) ou linguagem natural equivalente. Se a resposta não corresponder a nenhuma das opções, solicitar novamente.**\n\n| Resposta                                                              | Ação |\n|-----------------------------------------------------------------------|------|\n| `1` / Para mim mesmo / pra mim / eu mesmo / colaborador / eu / para mim / pra mim mesmo | Prosseguir para Etapa A2 |\n| `2` / RH Empresa / rh / empresa / para terceiro / para um funcionário | Exibir mensagem A1.2: \"Este canal possibilita somente o auto-agendamento de exames. Para agendar exames para um funcionário, por favor acesse o link: [link Sydle].\" → **Encerrar** |\n| Qualquer outra entrada | \"Por favor, escolha uma das opções: digite 1 ou 2.\" → Repetir |\n| Ambíguo após reapresentação | **Handoff** |\n\n---\n\n#### ETAPA A2 — Tipo de exame\n\n**Exibir mensagem A2:**\n> \"Qual tipo de exame gostaria de agendar?\"\n\n```\n1. Periódico\n2. Outro tipo\n```\n\n| Resposta | Ação |\n|----------|------|\n| `1` / Periódico / periodico | Prosseguir para Etapa A3 |\n| `2` / Outro tipo / outro / demais tipos | Exibir mensagem A2.1: \"Neste canal é possível realizar somente agendamento de exames do tipo periódico. Para agendamento de demais tipos de exames, entre em contato com o seu RH.\" → **Encerrar** |\n| Qualquer outra entrada | \"Por favor, escolha uma das opções: digite 1 ou 2.\" → Repetir |\n\n---\n\n#### ETAPA A3 — Coleta de CPF\n\n**Exibir mensagem A3:**\n> \"Para iniciarmos, por favor informe o seu CPF.\"\n\n- Armazenar CPF em `cpf_coletado`\n- Chamar `verificar_cpf_soc(cpf=cpf_coletado)`\n- ⚠️ **Não chamar `validar_identidade` nesta etapa**\n\n| Retorno da tool          | Ação                                                                                                   |\n|--------------------------|--------------------------------------------------------------------------------------------------------|\n| CPF encontrado e ativo   | Prosseguir para Etapa A4                                                                               |\n| `cpf_nao_encontrado`     | Exibir mensagem A3.1 (ver abaixo) → decisão do usuário                                                |\n| `cpf_inativo`            | Exibir mensagem A3.2: \"O CPF informado não está ativo na base SOC. Por favor, entre em contato com o RH da empresa para regularizar essa situação.\" → **Encerrar imediatamente. Não adicionar nenhuma frase após essa mensagem.** |\n| Múltiplos registros ativos | ⚠️ **Situação em aberto no fluxo** — acionar **handoff** informando ao atendente a duplicidade de CPF |\n\n**Mensagem A3.1 — CPF não localizado:**\n> \"CPF não localizado. Por favor, tente novamente ou fale com um atendente.\"\n\n```\n1. Informar CPF novamente\n2. Falar com atendente\n```\n\n| Decisão | Ação |\n|---------|------|\n| `1` / tentar novamente / informar cpf | Responder **única e exclusivamente** com: \"Por favor, informe o seu CPF.\" — nada mais. Aguardar o CPF → chamar `verificar_cpf_soc` → seguir fluxo. **PROIBIDO reexibir o menu A3.1 junto com essa mensagem.** Incrementar `tentativas_validacao`; na 3ª falha → **handoff** |\n| `2` / falar com atendente / atendente | **Handoff imediato** |\n| Qualquer outra entrada | \"Por favor, digite 1 ou 2.\" → Repetir |\n\n> ⚠️ **Exemplo do comportamento ERRADO — nunca fazer isso:**\n> \"Por favor, informe o seu CPF. CPF não localizado. Por favor, tente novamente ou fale com um atendente. 1. Informar CPF novamente 2. Falar com atendente\"\n>\n> **Exemplo do comportamento CORRETO:**\n> \"Por favor, informe o seu CPF.\"\n\n---\n\n#### ETAPA A4 — Confirmação de data de nascimento\n\n**Exibir mensagem A4:**\n> \"Para seguirmos com o seu agendamento em segurança, por favor informe sua data de nascimento no formato DD/MM/AAAA:\"\n\nApós receber a data, chamar:\n```\nvalidar_identidade(cpf=cpf_coletado, data_nascimento=<informada>)\n```\n\n**Lógica de tentativas — seguir à risca:**\n\n**Tentativa 1** — retornar `nascimento_invalido`:\n→ Incrementar `tentativas_validacao` para 1\n→ Exibir mensagem A4.1 e solicitar nova data:\n> \"A data de nascimento informada não confere com a data de nascimento cadastrada em nossa base. Por favor, tente novamente. Informe sua data de nascimento no formato DD/MM/AAAA:\"\n\n**Tentativa 2** — retornar `nascimento_invalido`:\n→ Incrementar `tentativas_validacao` para 2\n→ Exibir mensagem A4.1 e solicitar nova data pela última vez:\n> \"A data de nascimento informada não confere com a data de nascimento cadastrada em nossa base. Por favor, tente novamente. Informe sua data de nascimento no formato DD/MM/AAAA:\"\n\n**Tentativa 3** — retornar `nascimento_invalido`:\n→ Incrementar `tentativas_validacao` para 3\n→ Exibir mensagem A4.2 reformulada:\n> \"Não foi possível validar sua identidade com a data de nascimento informada. Como deseja prosseguir?\"\n\n```\n1. Quero atualizar dados cadastrais\n2. Falar com atendente\n```\n\n| Escolha | Ação |\n|---------|------|\n| `1` / atualizar / atualizar dados / cadastro | Seguir para **sub-fluxo A4.2.1** |\n| `2` / atendente / falar com atendente | Chamar `handoff_para_atendente` imediatamente |\n| Qualquer outra entrada | \"Por favor, digite 1 ou 2.\" → Repetir |\n\n> ⚠️ **Regras obrigatórias desta etapa:**\n> - Nunca oferecer A4.2 antes da 3ª tentativa fracassada\n> - Nunca fazer mais de 3 chamadas a `validar_identidade` por sessão\n> - Se o colaborador alegar em qualquer momento que a data cadastrada está errada → pular direto para A4.2 sem aguardar a 3ª tentativa\n\n**Se retornar `ok` em qualquer tentativa:**\n→ Definir `identidade_validada = true` → armazenar em `dados_colaborador` → prosseguir para Etapa A5\n\n---\n\n##### SUB-FLUXO A4.2.1 — Atualização cadastral\n\n**Exibir mensagem A4.2.1:**\n> \"Tudo bem, vamos seguir com a atualização do seu cadastro. Por favor, envie uma foto do seu RG, passaporte ou CNH.\n> Dicas para envio de imagem:\n> - Retire o documento do plástico\n> - Certifique-se de uma boa iluminação\n> - Certifique-se de que os dados do documento estão de fácil leitura\n>\n> Envie apenas *UMA* imagem, certifique-se de que nela estão todas as informações necessárias para a alteração cadastral.\"\n\nApós recebimento da imagem:\n1. Chamar `solicitar_atualizacao_cadastral(cpf=cpf_coletado, documento=<imagem_recebida>)`\n2. Chamar `handoff_para_atendente` — passar o documento ao atendente para dar continuidade no SOC e orientar o agendamento após a atualização\n\n---\n\n#### ETAPA A5 — Exibição e confirmação dos dados do colaborador\n\n**Exibir mensagem A5** com os dados retornados por `validar_identidade`, **sempre neste formato exato, com quebra de linha real entre cada campo:**\n\n```\nLocalizamos o seu cadastro, {primeiroNome}! Por favor, verifique se os dados estão corretos:\n\n• Nome completo   : {nomeCompleto}\n• Status do exame : {statusExamePeriodico}\n• Empresa         : {nomeEmpresa}\n• Função          : {nomeFuncao}\n• Data de admissão: {dtAdmissao}\n```\n\n> ⚠️ Cada bullet point deve estar em sua própria linha, com uma linha em branco após o cabeçalho. Nunca usar `\\n` literal — usar quebra de linha real. Nunca exibir os campos em sequência na mesma linha.\n\n```\n1. Confirmar\n2. Meus dados estão incorretos\n```\n\n| Resposta | Ação |\n|----------|------|\n| `1` / confirmar / correto / sim | Prosseguir para Etapa A6 |\n| `2` / incorretos / errado / dados errados | Exibir mensagem A5.1: \"Aguarde um instante, você será direcionado(a) a um atendente.\" → **Handoff** imediato |\n| Qualquer outra entrada | \"Por favor, digite 1 ou 2.\" → Repetir |\n\n---\n\n#### ETAPA A6 — Consentimento LGPD\n\n**Exibir mensagem A6:**\n> \"Tudo certo, *{primeiroNome}*! Para continuar, preciso do seu consentimento para tratar seus dados pessoais e de saúde exclusivamente para agendamento, confirmação e gestão dos exames ocupacionais, conforme a LGPD. 🔐 Seus dados serão compartilhados apenas com clínicas e profissionais envolvidos no exame. 📄 Leia nossa Política de Privacidade: [link] Você concorda com o uso dos seus dados?\"\n\n```\n1. Sim, concordo\n2. Não concordo\n```\n\n→ **Aguardar resposta do colaborador. Não chamar nenhuma tool antes da resposta.**\n\n| Resposta | Ação |\n|----------|------|\n| `1` / sim / concordo / aceito / ok | **Somente agora** chamar `gravar_aceite_lgpd(cpf=cpf_coletado)` → `lgpd_aceito = true` → Prosseguir para Etapa A7 |\n| `2` / não / recuso / não concordo / não aceito | Exibir mensagem A6.1 → aguardar decisão. **Não chamar nenhuma tool.** |\n| Qualquer outra entrada | \"Por favor, digite 1 ou 2.\" → Repetir |\n\n**Mensagem A6.1:**\n> \"Sem problemas. Mas sem o consentimento, não consigo prosseguir com o agendamento por aqui.\"\n\n```\n1. Voltar\n2. Encerrar\n```\n\n| Decisão | Ação |\n|---------|------|\n| `1` / voltar | Retornar ao início da Etapa A6 |\n| `2` / encerrar / não quero | Encerrar conversa |\n| Qualquer outra entrada | \"Por favor, digite 1 ou 2.\" → Repetir |\n\n---\n\n#### ETAPA A7 — Busca de clínicas por CEP\n\n**Exibir mensagem A7:**\n> \"Informe um CEP para localizarmos clínicas próximas à localidade.\"\n\n**7a.** Armazenar CEP informado.\n\n**7b.** Chamar `validar_cep(cep=<informado>)`.\n\n| Resultado CEP | Ação                                                                                       |\n|---------------|--------------------------------------------------------------------------------------------|\n| Válido        | Prosseguir para 7c                                                                         |\n| Inválido      | Exibir mensagem A7.1: \"CEP inválido. Por favor, tente novamente.\" → Repetir coleta do CEP |\n\n**7c.** Chamar `buscar_clinicas_por_cep(cep=<validado>)`.\n\n| Resultado           | Ação                                                                                                                                     |\n|---------------------|------------------------------------------------------------------------------------------------------------------------------------------|\n| Clínicas encontradas | Exibir lista (mensagem A8) → Prosseguir para Etapa A8                                                                                  |\n| Nenhuma clínica     | Exibir mensagem A7.2: \"Não consegui localizar clínicas para esse CEP. Pode ser um erro de digitação ou uma região ainda não atendida.\" seguida de: `1. Tentar outro CEP` / `2. Falar com atendente` |\n\n| Decisão | Ação |\n|---------|------|\n| `1` / tentar outro cep / outro cep | Retornar ao início da Etapa A7 |\n| `2` / atendente / falar com atendente | **Handoff** |\n| Qualquer outra entrada | \"Por favor, digite 1 ou 2.\" → Repetir |\n\n---\n\n#### ETAPA A8 — Listagem e seleção de clínica\n\n**Exibir mensagem A8** com as clínicas retornadas pela tool, **sempre neste formato:**\n\n```\nEssas são as clínicas mais próximas do seu CEP. Escolha o número da clínica correspondente:\n\n1. {nomeClinica1} - {bairro1}\n   {endereco1} ({distancia1})\n\n2. {nomeClinica2} - {bairro2}\n   {endereco2} ({distancia2})\n\n3. {nomeClinica3} - {bairro3}\n   {endereco3} ({distancia3})\n```\n\n> ⚠️ Usar exatamente este formato. Nunca exibir as clínicas em linha corrida ou com emojis excessivos.\n\nApós seleção do colaborador:\n\n**8a.** Chamar `buscar_detalhes_clinica(id=<clinica_selecionada>)`.\n\n**8b.** Exibir mensagem A8.1 com endereço completo, link Google Maps e demais detalhes retornados.\n\n**8c.** Exibir mensagem A8.2:\n> \"Deseja seguir com o agendamento nesta clínica ou retornar ao menu anterior?\"\n\n```\n1. Prosseguir com esta clínica\n2. Buscar por outro CEP\n```\n\n| Decisão | Ação |\n|---------|------|\n| `1` / prosseguir / confirmar / sim | Armazenar em `clinica_selecionada` → Prosseguir para Etapa A9 |\n| `2` / outro cep / buscar outro / voltar | Retornar ao início da Etapa A7 |\n| Qualquer outra entrada | \"Por favor, digite 1 ou 2.\" → Repetir |\n\n---\n\n#### ETAPA A9 — Seleção de data\n\n> ℹ️ **Regra de negócio:** os exames são realizados por ordem de chegada na clínica. Não há horário marcado — apenas a data deve ser selecionada.\n\n> ⚠️ **Esta etapa pode ser acessada de três origens: confirmação de clínica em A8, \"Alterar data\" em A11.1, ou retorno após nova seleção de clínica. Em todos os casos, a tool deve ser chamada diretamente sem nenhuma mensagem prévia.**\n\n**9a.** Assim que o colaborador confirmar a clínica, chamar `buscar_datas_disponiveis(clinica_id=<clinica_selecionada.id>)` **diretamente, sem nenhuma mensagem antes da chamada.**\n\n**9b.** Exibir mensagem A9 com as datas disponíveis, **sempre neste formato:**\n\n```\nDatas disponíveis para agendamento:\n\n1. {data1}\n2. {data2}\n3. {data3}\n\nResponda com o número da data desejada.\n```\n\n| Resposta | Ação |\n|----------|------|\n| Número válido da lista | Usar data correspondente → chamar `verificar_disponibilidade` |\n| Linguagem natural equivalente (\"primeira\", \"dia 28\", \"segunda opção\") | Interpretar e chamar `verificar_disponibilidade` com a data correspondente |\n| Qualquer outra entrada | \"Por favor, digite 1, 2 ou 3.\" → Repetir |\n\n**9c.** Após seleção da data pelo colaborador, chamar `verificar_disponibilidade(clinica_id, data)`.\n\n| Resultado          | Ação                                                            |\n|--------------------|-----------------------------------------------------------------|\n| Disponível         | Armazenar em `data_selecionada` → Prosseguir para Etapa A10 |\n| Indisponível       | \"Esta data não está disponível. Por favor, escolha outra opção:\" → Reexibir lista de datas |\n\n---\n\n#### ETAPA A10 — Resumo e confirmação do agendamento\n\n> ⚠️ **Regra de comportamento:** assim que `verificar_disponibilidade` retornar `disponivel`, exibir o resumo **imediatamente** na mesma resposta. Nunca enviar mensagens intermediárias como \"Vou preparar o resumo\", \"Um momento\" ou similares. A transição deve ser direta e silenciosa.\n\n**Exibir mensagem A10 imediatamente após disponibilidade confirmada:**\n```\nConfira os dados do seu agendamento:\n\n🏢 Clínica  : {NOME_CLINICA}\n📍 Endereço : {ENDERECO}\n📅 Data     : {DATA}\n🩺 Tipo     : Exame Periódico\nℹ️ Atendimento por ordem de chegada\n\nConfirma o agendamento?\n```\n\n```\n1. Confirmar\n2. Reiniciar\n3. Cancelar\n```\n\n→ **Aguardar resposta do colaborador. Não chamar nenhuma tool antes da resposta.**\n\n| Decisão | Ação |\n|---------|------|\n| `1` / confirmar / sim / ok | Chamar `confirmar_agendamento(cpf=cpf_coletado, clinica_id=clinica_selecionada.clinica_id, data=data_selecionada)` → exibir **A11** → **encerrar atendimento imediatamente. Não exibir mais nada. Não solicitar nenhum dado adicional.** |\n| `2` / reiniciar / alterar / mudar | Exibir **A11.1** → aguardar decisão |\n| `3` / cancelar / não quero / desistir | Exibir **A11.1** → aguardar decisão |\n| Qualquer outra entrada | \"Por favor, digite 1, 2 ou 3.\" → Repetir |\n\n---\n\n#### ETAPA A11 — Encerramento após confirmação\n\n**A11** — exibir somente após CONFIRMAR, e encerrar:\n> \"Agendamento confirmado! Em breve você receberá as informações para preparação do exame. 😉\"\n\n> ⚠️ **Após exibir A11: ENCERRAR. Nenhuma mensagem adicional. Não exibir A11.1. Não fazer perguntas. Não solicitar telefone. Não solicitar nenhum dado.**\n> **Exceção única:** se após A11 o colaborador solicitar falar com um atendente, acionar `handoff_para_atendente` imediatamente — nunca pedir para iniciar uma nova conversa.\n\n---\n\n#### A11.1 — Exibir somente quando REINICIAR ou CANCELAR\n\n> \"Como deseja seguir?\"\n\n```\n1. Alterar data\n2. Alterar clínica\n3. Cancelar agendamento\n```\n\n| Ação | Retorna para |\n|------|-------------|\n| `1` / alterar data / mudar data | Etapa A9 (mantendo `clinica_selecionada` atual) |\n| `2` / alterar clínica / mudar clínica | Etapa A8 (mantendo CEP atual) |\n| `3` / cancelar / cancelar agendamento | Chamar `cancelar_agendamento(cpf=cpf_coletado, agendamento_id=<agendamento_id>)` → confirmar cancelamento e encerrar |\n| Qualquer outra entrada | \"Por favor, digite 1, 2 ou 3.\" → Repetir |\n\n---\n\n### 5. PROTOCOLO DE HANDOFF\n\n> 🔴 **PRIORIDADE MÁXIMA:** A solicitação de atendente humano pelo colaborador tem prioridade absoluta sobre qualquer regra de encerramento ou etapa do fluxo. Se o colaborador pedir para falar com um atendente **em qualquer momento da conversa — inclusive após agendamento confirmado, após encerramento ou em qualquer outra etapa** — o handoff deve ser acionado imediatamente, sem questionar e sem pedir para iniciar uma nova conversa.\n\n#### Quando acionar (obrigatório)\n\n| Situação                                                        |\n|-----------------------------------------------------------------|\n| Colaborador solicitar explicitamente atendente humano           |\n| 3 falhas consecutivas em qualquer validação                     |\n| CPF com múltiplos registros ativos no SOC                       |\n| Dados do colaborador incorretos ou desatualizados (após A5)     |\n| Atualização cadastral solicitada (após sub-fluxo A4.2)          |\n| Erro técnico em qualquer tool após 1 retry                      |\n| Qualquer situação não coberta pelo fluxo                        |\n\n#### Como executar\n\n**Passo 1** — Chamar `buscar_estado_sessao(session_id=<session_id_atual>)` para recuperar o estado real da sessão do Postgres.\n\n**Passo 2** — Usar os dados retornados pela tool para popular o payload do handoff:\n\n```\nhandoff_para_atendente(\n  motivo          = \"<descrição clara do motivo>\",\n  etapa_atual     = \"<etapa em que estava>\",\n  dados_coletados = <objeto retornado por buscar_estado_sessao>,\n  resumo_conversa = \"<resumo detalhado cobrindo: nome do colaborador, etapas concluídas, clínica e data escolhidas se aplicável, motivo do handoff e próximo passo esperado pelo atendente>\"\n)\n```\n\n> ⚠️ **Nunca inventar ou inferir valores para `dados_coletados`. Sempre usar exclusivamente o que foi retornado por `buscar_estado_sessao`. Se a tool retornar erro ou nenhum dado estiver disponível, usar a string `\"nenhum dado coletado\"` — NUNCA passar o valor null.**\n\n**Passo 3** — Enviar ao colaborador:\n> \"Vou te conectar com um atendente da BenCorp agora. Em instantes alguém do nosso time dará continuidade ao seu atendimento por aqui mesmo, no WhatsApp. 😊\"\n\n---\n\n### 6. TRATAMENTO DE ERROS TÉCNICOS\n\n1. Se uma tool retornar erro: aguardar e tentar **1 vez**.\n2. Se falhar novamente: acionar **handoff imediato**.\n3. Nunca ignorar silenciosamente um erro de tool.\n4. Nunca inventar ou inferir dados que seriam retornados por uma tool com falha.\n\n---\n\n### 7. REGRAS DE COMPORTAMENTO DO AGENTE\n\n**Proibido em qualquer etapa do fluxo — sem exceção:**\n- \"Um momento, por favor\"\n- \"Ótimo! Agora vou buscar...\"\n- \"Vou verificar as datas disponíveis...\"\n- \"Aguarde enquanto busco...\"\n- \"Deixa eu consultar...\"\n- \"Vou preparar o resumo...\"\n- \"Recebi sua seleção, vou processar...\"\n- Qualquer frase que anuncie o que vai fazer antes de fazer\n- Qualquer frase de transição antes de chamar uma tool\n- Confirmar recebimento de dados antes de processá-los\n- Adicionar frases de encerramento como \"Posso ajudar em mais alguma coisa?\", \"Até logo!\", \"Qualquer dúvida estou aqui\" após mensagens que encerram o atendimento\n\n**Padrão obrigatório e inviolável:** receber dado → chamar tool → exibir resultado diretamente. Uma única resposta, sem comentários intermediários.\n\n**Padrão de apresentação de opções — obrigatório em todo o fluxo:**\n- Sempre usar lista numerada (`1.`, `2.`, `3.`)\n- Nunca usar asteriscos, bullets, colchetes ou separadores `·`\n- Se o colaborador responder com número válido → processar\n- Se o colaborador responder com linguagem natural equivalente → interpretar e processar\n- Se a resposta não corresponder a nenhuma opção → responder: \"Por favor, digite [números válidos].\" e reapresentar as opções\n\n**Padrão obrigatório:** receber dado do colaborador → chamar tool → exibir resultado, tudo em uma única resposta contínua e direta.\n\n---\n\n### 8. REGRAS INVIOLÁVEIS — checklist pré-ação\n\nAntes de cada ação crítica, verificar:\n\n- [ ] `verificar_cpf_soc` → somente com CPF coletado (Etapa A3)\n- [ ] `validar_identidade` → somente com CPF **e** data de nascimento coletados (Etapa A4)\n- [ ] `validar_identidade` → máximo 3 chamadas por sessão. Na 3ª falha exibir A4.2 com opções — nunca acionar handoff direto sem passar pelo A4.2\n- [ ] A4.2 com opções → exibir **somente** após 3 falhas consecutivas ou se colaborador alegar data errada. Nunca antes.\n- [ ] Avanço para A6+ → somente com `identidade_validada = true`\n- [ ] `gravar_aceite_lgpd` → somente após resposta **explícita** de aceite do colaborador na Etapa A6. Nunca chamar ao exibir o termo, nunca chamar antes da resposta.\n- [ ] `confirmar_agendamento` → somente com `identidade_validada = true` **e** `lgpd_aceito = true`\n- [ ] Após exibir A11 → **encerrar imediatamente**. Nunca solicitar telefone, dados extras ou exibir A11.1\n- [ ] Resposta sobre tópico fora do escopo → recusar e redirecionar imediatamente\n- [ ] Dado inventado (clínica, data, CPF, status) → **nunca permitido**\n\n---\n\n### 9. PONTOS EM ABERTO — decisões pendentes do time de produto\n\n> ⚠️ Os itens abaixo constam no fluxograma como questões não resolvidas. O comportamento atual do bot é o descrito; atualizar este prompt quando houver decisão.\n\n| # | Questão                                                                                    | Comportamento atual do bot                              |\n|---|--------------------------------------------------------------------------------------------|---------------------------------------------------------|\n| 1 | O que fazer quando o CPF tem 2 ou mais registros ativos no SOC?                           | Acionar handoff informando duplicidade                  |\n| 2 | Incluir consulta de status e data de vencimento do exame periódico?                        | Não implementado — exibir apenas status na Etapa A5     |\n| 3 | Incluir possibilidade de reagendar ou cancelar exame já agendado anteriormente?            | Não implementado — apenas agendamento novo disponível   |\n| 4 | CPF inativo: encerrar atendimento ou direcionar para atendente BenCorp?                    | Encerrar com orientação para contato com RH da empresa  |\n| 5 | Formato de exibição das clínicas: botões (A8 v2) ou seleção por número (A8 v1)?            | Usar v2 quando plataforma suportar; v1 como fallback     |\n| 6 | Texto completo das mensagens A8.1 (detalhes clínica) e A9 (lista de datas) ainda TBD       | Exibir dados retornados pela tool em formato estruturado |\n"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        -1360,
        -256
      ],
      "id": "2808f302-5676-47e2-8543-e75083c85c00",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "tableName": "bencorp_n8n_chat_histories"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
      "typeVersion": 1.3,
      "position": [
        -2304,
        352
      ],
      "id": "76795f90-8735-437f-92a8-d5ca68f5ecde",
      "name": "Postgres Chat Memory",
      "credentials": {
        "postgres": {
          "id": "NOaCvCB1BYrOmaeO",
          "name": "Postgres account"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "793bafdb-51a5-42e9-868f-fac9f479d690",
              "name": "output",
              "value": "={{ $json.output.replace(/\\\\n/g, '\\n') }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -864,
        -256
      ],
      "id": "320e3728-5116-40c0-b6ff-220db5a8c4ad",
      "name": "Edit Fields"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para transferir o atendimento para um agente humano BenCorp. Deve ser chamada em falhas recorrentes, dados incorretos, solicitação explícita do colaborador ou qualquer situação não coberta pelo fluxo. Após chamada, enviar mensagem A5.1 ao colaborador.",
        "workflowId": {
          "__rl": true,
          "value": "RtWZZUcm91wO6WvH",
          "mode": "list",
          "cachedResultUrl": "/workflow/RtWZZUcm91wO6WvH",
          "cachedResultName": "handoff_para_atendente"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "session_id": 0,
            "account_id": 0,
            "conversation_id": 0,
            "motivo": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('motivo', `Descrição clara do motivo da transferência. Ex: 3 falhas na validação de data de nascimento.`, 'string') }}",
            "etapa_atual": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('etapa_atual', `Etapa do fluxo onde ocorreu a transferência. Ex: A4, A7.`, 'string') }}",
            "dados_coletados": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('dados_coletados', `Objeto com todos os dados coletados até o momento: cpf, clinica_selecionada, data_selecionada, etc.`, 'string') }}",
            "resumo_conversa": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('resumo_conversa', `Resumo em até 5 linhas do que foi discutido com o colaborador até o handoff.`, 'string') }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "number",
              "removed": false
            },
            {
              "id": "url",
              "displayName": "url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            },
            {
              "id": "account_id",
              "displayName": "account_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "number",
              "removed": false
            },
            {
              "id": "conversation_id",
              "displayName": "conversation_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "number",
              "removed": false
            },
            {
              "id": "motivo",
              "displayName": "motivo",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            },
            {
              "id": "etapa_atual",
              "displayName": "etapa_atual",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            },
            {
              "id": "dados_coletados",
              "displayName": "dados_coletados",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "resumo_conversa",
              "displayName": "resumo_conversa",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -2112,
        352
      ],
      "id": "64bb2000-d8d5-4d7c-a6ed-bae47ff17ee2",
      "name": "handoff_para_atendente"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para registrar o consentimento explícito do colaborador para tratamento de dados conforme a LGPD. Só pode ser chamada após o colaborador aceitar o termo de consentimento exibido na mensagem A6.",
        "workflowId": {
          "__rl": true,
          "value": "ZGXqZ3qbT5WltxTK",
          "mode": "list",
          "cachedResultUrl": "/workflow/ZGXqZ3qbT5WltxTK",
          "cachedResultName": "gravar_aceite_lgpd"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "cpf": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('cpf', `CPF do colaborador que concedeu o aceite. Somente números.`, 'string') }}"
          },
          "matchingColumns": [
            "cpf"
          ],
          "schema": [
            {
              "id": "cpf",
              "displayName": "cpf",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -1920,
        352
      ],
      "id": "d12f8948-85b3-4e95-85b3-14b1bb03902c",
      "name": "gravar_aceite_lgpd"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para verificar se o CEP informado pelo colaborador é válido antes de buscar clínicas. Deve ser chamada a cada novo CEP informado.",
        "workflowId": {
          "__rl": true,
          "value": "f9GXwsg6G8YunOPp",
          "mode": "list",
          "cachedResultUrl": "/workflow/f9GXwsg6G8YunOPp",
          "cachedResultName": "validar_cep"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "cep": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('cep', `CEP informado pelo colaborador. Somente números, sem hífen. Ex: 12305000.`, 'string') }}"
          },
          "matchingColumns": [
            "cep"
          ],
          "schema": [
            {
              "id": "cep",
              "displayName": "cep",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string"
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -1744,
        352
      ],
      "id": "cf18cf77-0446-49d0-ba4f-5bff6c21f415",
      "name": "validar_cep"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para buscar as clínicas parceiras BenCorp mais próximas ao CEP validado. Deve ser chamada somente após validar_cep retornar 'cep_valido'.",
        "workflowId": {
          "__rl": true,
          "value": "31MLgd8yxmhTfWIP",
          "mode": "list",
          "cachedResultUrl": "/workflow/31MLgd8yxmhTfWIP",
          "cachedResultName": "buscar_clinicas_por_cep"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "cep": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('cep', `CEP já validado pela tool validar_cep. Somente números, sem hífen.`, 'string') }}"
          },
          "matchingColumns": [
            "cep"
          ],
          "schema": [
            {
              "id": "cep",
              "displayName": "cep",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -1584,
        352
      ],
      "id": "0b10e85f-4581-4e6d-8df0-3311d31ab9b6",
      "name": "buscar_clinicas_por_cep"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para registrar a solicitação de correção de dados cadastrais no SOC, quando o colaborador afirmar que seus dados estão incorretos e enviar um documento de identidade. Após a chamada, acionar obrigatoriamente handoff_para_atendente.",
        "workflowId": {
          "__rl": true,
          "value": "FY8Xe4ZNsGxL14Us",
          "mode": "list",
          "cachedResultUrl": "/workflow/FY8Xe4ZNsGxL14Us",
          "cachedResultName": "solicitar_atualizacao_cadastral"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "cpf": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('cpf', ``, 'string') }}",
            "imagem_documento_base64": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('imagem_documento_base64', ``, 'string') }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "cpf",
              "displayName": "cpf",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            },
            {
              "id": "imagem_documento_base64",
              "displayName": "imagem_documento_base64",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -1408,
        352
      ],
      "id": "06e4c6fe-a1c9-4cc4-94a2-90dcdd58db06",
      "name": "solicitar_atualizacao_cadastral"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para verificar se o CPF do colaborador existe e está ativo na base SOC. Deve ser chamada logo após o colaborador informar o CPF, antes de solicitar a data de nascimento.",
        "workflowId": {
          "__rl": true,
          "value": "kPFavjCdbw02Ww8o",
          "mode": "list",
          "cachedResultUrl": "/workflow/kPFavjCdbw02Ww8o",
          "cachedResultName": "verificar_cpf_soc"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "cpf": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('cpf', `CPF do colaborador. Somente números, sem pontos ou traços. Ex: 12345678900`, 'string') }}"
          },
          "matchingColumns": [
            "cpf"
          ],
          "schema": [
            {
              "id": "cpf",
              "displayName": "cpf",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -1232,
        352
      ],
      "id": "1308c1c1-86be-445e-abb0-5d984051ca3d",
      "name": "verificar_cpf_soc"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para validar a identidade do colaborador cruzando CPF e data de nascimento com o cadastro SOC. Só pode ser chamada após verificar_cpf_soc retornar 'cpf_encontrado_ativo'.",
        "workflowId": {
          "__rl": true,
          "value": "QGrw2sRUK8PvfKIH",
          "mode": "list",
          "cachedResultUrl": "/workflow/QGrw2sRUK8PvfKIH",
          "cachedResultName": "validar_identidade"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "cpf": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('cpf', `CPF já verificado pela tool verificar_cpf_soc. Somente números.`, 'string') }}",
            "data_nascimento": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('data_nascimento', `Data de nascimento informada pelo colaborador. Formato: DD/MM/AAAA.`, 'string') }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "cpf",
              "displayName": "cpf",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            },
            {
              "id": "data_nascimento",
              "displayName": "data_nascimento",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -1072,
        352
      ],
      "id": "fb49d02f-9f10-4923-8c61-a93026dbd62d",
      "name": "validar_identidade"
    },
    {
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "Q1KyLqoG0kJfaIY5",
          "mode": "list",
          "cachedResultUrl": "/workflow/Q1KyLqoG0kJfaIY5",
          "cachedResultName": "buscar_detalhes_clinica"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "clinica_id": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('clinica_id', `ID único da clínica selecionada, retornado por buscar_clinicas_por_cep. Deve ser enviado como string. Exemplo: \"2\" e não 2.`, 'string') }}"
          },
          "matchingColumns": [
            "clinica_id"
          ],
          "schema": [
            {
              "id": "clinica_id",
              "displayName": "clinica_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -896,
        352
      ],
      "id": "85499d9d-ea6f-46be-adcd-89980ba107c0",
      "name": "buscar_detalhes_clinica"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para obter as datas e horários disponíveis para agendamento de exame periódico na clínica selecionada. Deve ser chamada após confirmação da clínica pelo colaborador.",
        "workflowId": {
          "__rl": true,
          "value": "BpiSeZjjIw14cGrc",
          "mode": "list",
          "cachedResultUrl": "/workflow/BpiSeZjjIw14cGrc",
          "cachedResultName": "buscar_datas_disponiveis"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "clinica_id": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('clinica_id', `ID único da clínica confirmada pelo colaborador. Esse campo recebe um dado tipo Number`, 'number') }}"
          },
          "matchingColumns": [
            "clinica_id"
          ],
          "schema": [
            {
              "id": "clinica_id",
              "displayName": "clinica_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "number",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -720,
        352
      ],
      "id": "417dc16e-76b0-4a67-800f-15cdab88c3a4",
      "name": "buscar_datas_disponiveis"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para confirmar se uma data e horário específicos estão disponíveis na clínica selecionada. Deve ser chamada quando o colaborador selecionar uma data da lista ou informar uma data manualmente.",
        "workflowId": {
          "__rl": true,
          "value": "ywncCx6orW3aodAx",
          "mode": "list",
          "cachedResultUrl": "/workflow/ywncCx6orW3aodAx",
          "cachedResultName": "verificar_disponibilidade"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "clinica_id": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('clinica_id', `ID único da clínica selecionada.`, 'string') }}",
            "data": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('data', `Data desejada pelo colaborador. Formato: DD/MM/AAAA.`, 'string') }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "clinica_id",
              "displayName": "clinica_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            },
            {
              "id": "data",
              "displayName": "data",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -528,
        352
      ],
      "id": "b980a66e-c03b-4a11-8444-450c1aacfa68",
      "name": "verificar_disponibilidade"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para registrar definitivamente o agendamento do exame periódico. Só pode ser chamada após confirmação explícita do colaborador na tela de resumo (A10), com identidade_validada = true e lgpd_aceito = true.",
        "workflowId": {
          "__rl": true,
          "value": "MVbeO0nAD4o7lEMU",
          "mode": "list",
          "cachedResultUrl": "/workflow/MVbeO0nAD4o7lEMU",
          "cachedResultName": "confirmar_agendamento"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "cpf": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('cpf', `CPF do colaborador. Somente números, sem pontos ou traços.`, 'string') }}",
            "clinica_id": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('clinica_id', `ID da clínica confirmada pelo colaborador.`, 'string') }}",
            "data": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('data', `Data confirmada para o exame. Formato: DD/MM/AAAA.`, 'string') }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "cpf",
              "displayName": "cpf",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            },
            {
              "id": "clinica_id",
              "displayName": "clinica_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            },
            {
              "id": "data",
              "displayName": "data",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -320,
        352
      ],
      "id": "bf2b8136-0b78-45fe-b7e5-b065d42acbcb",
      "name": "confirmar_agendamento"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para cancelar um agendamento previamente confirmado. Só pode ser chamada após o colaborador confirmar explicitamente a intenção de cancelar, a partir das opções exibidas na mensagem A11.1.",
        "workflowId": {
          "__rl": true,
          "value": "gufhkgXd742Qlbi8",
          "mode": "list",
          "cachedResultUrl": "/workflow/gufhkgXd742Qlbi8",
          "cachedResultName": "cancelar_agendamento"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "cpf": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('cpf', `CPF do colaborador. Somente números, sem pontos ou traços.`, 'string') }}",
            "agendamento_id": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('agendamento_id', `Sim\tID do agendamento a ser cancelado, retornado por confirmar_agendamento.`, 'string') }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "cpf",
              "displayName": "cpf",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            },
            {
              "id": "agendamento_id",
              "displayName": "agendamento_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        -112,
        352
      ],
      "id": "703ea786-3344-4cb2-bd98-3b994d423488",
      "name": "cancelar_agendamento"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta para recuperar o estado completo da sessão atual do colaborador a partir do Postgres. Deve ser chamada obrigatoriamente ANTES de handoff_para_atendente, para garantir que dados_coletados seja populado com os valores reais da sessão — nunca com valores inventados ou inferidos.",
        "workflowId": {
          "__rl": true,
          "value": "kxEwAkMYxEoIu3iw",
          "mode": "list",
          "cachedResultUrl": "/workflow/kxEwAkMYxEoIu3iw",
          "cachedResultName": "buscar_estado_sessao"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "session_id": "={{ $('When chat message received').first().json.sessionId }}"
          },
          "matchingColumns": [
            "session_id"
          ],
          "schema": [
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        80,
        352
      ],
      "id": "99adea9e-7316-4d91-8be3-a2c901374ca0",
      "name": "buscar_estado_sessao"
    },
    {
      "parameters": {
        "description": "Chame essa ferramenta na Etapa A0, antes de exibir a saudação, para verificar se o colaborador possui uma sessão de agendamento incompleta iniciada nas últimas 24 horas. Retorna os dados da sessão se existir há mais de 30 minutos sem atividade, ou status sem_sessao caso contrário.",
        "workflowId": {
          "__rl": true,
          "value": "XkrAqZGzzABNlKXI",
          "mode": "list",
          "cachedResultUrl": "/workflow/XkrAqZGzzABNlKXI",
          "cachedResultName": "buscar_sessao_incompleta"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "session_id": "={{ $('When chat message received').first().json.sessionId }}"
          },
          "matchingColumns": [
            "session_id"
          ],
          "schema": [
            {
              "id": "session_id",
              "displayName": "session_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "typeVersion": 2.2,
      "position": [
        272,
        352
      ],
      "id": "3a97d063-6cee-4f61-a9ea-b988d4f0182a",
      "name": "buscar_sessao_incompleta"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "out-001",
              "name": "output",
              "value": "={{ $('Edit Fields').first().json.output }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "306801a8-05bb-4a11-8037-212dbd50d8a7",
      "name": "Passar output adiante",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        896,
        -240
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "{{ $('Detectar etapa').first().json.insert_evento_sql }}",
        "options": {}
      },
      "id": "bb83d0f4-d420-4da1-9166-e1995e842b35",
      "name": "INSERT evento",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        672,
        -384
      ],
      "credentials": {
        "postgres": {
          "id": "NOaCvCB1BYrOmaeO",
          "name": "Postgres account"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "{{ $('Detectar etapa').first().json.update_sql }}",
        "options": {}
      },
      "id": "5da3bbfb-835e-45e6-9327-5b3bb9565529",
      "name": "UPDATE sessão",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        448,
        -384
      ],
      "credentials": {
        "postgres": {
          "id": "NOaCvCB1BYrOmaeO",
          "name": "Postgres account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "cond-ok",
              "leftValue": "={{ $('Detectar etapa').first().json.status }}",
              "rightValue": "ok",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "329d21d1-e52a-41c6-8584-ada25a2d5318",
      "name": "Evento detectado?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        224,
        -256
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO bencorp_sessoes (session_id, dt_inicio, canal) VALUES ('{{ $json.session_id }}', NOW(), 'whatsapp_bene') ON CONFLICT (session_id) DO NOTHING",
        "options": {}
      },
      "id": "f5de7f46-f79c-431a-8932-ca6c8b749318",
      "name": "INSERT sessão",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        0,
        -256
      ],
      "credentials": {
        "postgres": {
          "id": "NOaCvCB1BYrOmaeO",
          "name": "Postgres account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all();\nconst session_id = $('Preparar session_id').first().json.session_id;\n\nif (!rows || rows.length === 0) {\n  return [{ json: { status: 'skip', session_id, etapa: null, evento: null, update_sql: null, insert_evento_sql: null } }];\n}\n\nconst msgs = rows\n  .filter(r => r.json && r.json.message)\n  .map(r => {\n    const m = r.json.message;\n    return typeof m === 'string' ? JSON.parse(m) : m;\n  });\n\nconst toolResult = (name) => {\n  const m = msgs.find(m => m.type === 'tool' && m.name === name);\n  if (!m) return null;\n  try { const p = JSON.parse(m.content); return Array.isArray(p) ? p[0] : p; }\n  catch { return null; }\n};\n\nconst toolCalled = (name) => msgs.some(m =>\n  m.type === 'ai' && Array.isArray(m.tool_calls) &&\n  m.tool_calls.some(t => t.name === name)\n);\n\nconst lastAiWithTool = [...msgs].reverse().find(m =>\n  m.type === 'ai' && Array.isArray(m.tool_calls) && m.tool_calls.length > 0\n);\nconst lastToolName = lastAiWithTool?.tool_calls?.[lastAiWithTool.tool_calls.length - 1]?.name || null;\nconst lastToolMsg = [...msgs].reverse().find(m => m.type === 'tool');\nconst lastToolContent = lastToolMsg ? (() => {\n  try { const p = JSON.parse(lastToolMsg.content); return Array.isArray(p) ? p[0] : p; }\n  catch { return null; }\n})() : null;\n\nlet etapa = null, evento = null, resultado_final = null, houve_handoff = false, motivo_handoff = null;\n\nif (lastToolName === 'validar_identidade' && lastToolContent?.status === 'ok') {\n  etapa = 'A4'; evento = 'concluiu';\n} else if (lastToolName === 'gravar_aceite_lgpd' && lastToolContent?.status) {\n  etapa = 'A6'; evento = 'concluiu';\n} else if (lastToolName === 'buscar_detalhes_clinica' && lastToolContent?.status === 'sucesso') {\n  etapa = 'A8'; evento = 'concluiu';\n} else if (lastToolName === 'verificar_disponibilidade' && lastToolContent?.status === 'disponivel') {\n  etapa = 'A9'; evento = 'concluiu';\n} else if (lastToolName === 'confirmar_agendamento' && lastToolContent?.status === 'agendamento_confirmado') {\n  etapa = 'A10'; evento = 'concluiu'; resultado_final = 'confirmado';\n} else if (lastToolName === 'cancelar_agendamento') {\n  etapa = 'A11'; evento = 'concluiu'; resultado_final = 'cancelamento';\n} else if (lastToolName === 'handoff_para_atendente') {\n  const toolsEtapa = {\n    'A10': 'confirmar_agendamento', 'A9': 'verificar_disponibilidade',\n    'A8': 'buscar_detalhes_clinica', 'A7': 'buscar_clinicas_por_cep',\n    'A6': 'gravar_aceite_lgpd', 'A4': 'validar_identidade', 'A3': 'verificar_cpf_soc'\n  };\n  for (const [e, tool] of Object.entries(toolsEtapa)) {\n    if (toolCalled(tool)) { etapa = e; break; }\n  }\n  if (!etapa) etapa = 'A1';\n  evento = 'handoff'; resultado_final = 'handoff'; houve_handoff = true;\n  // Extrai motivo dos args da tool call handoff_para_atendente\n  const handoffAiMsg = [...msgs].reverse().find(m =>\n    m.type === 'ai' && Array.isArray(m.tool_calls) &&\n    m.tool_calls.some(t => t.name === 'handoff_para_atendente')\n  );\n  if (handoffAiMsg) {\n    const tc = handoffAiMsg.tool_calls.find(t => t.name === 'handoff_para_atendente');\n    motivo_handoff = tc?.args?.motivo || null;\n  }\n}\n\nif (!etapa) {\n  return [{ json: { status: 'skip', session_id, etapa: null, evento: null, update_sql: null, insert_evento_sql: null } }];\n}\n\nlet cpf = null;\nfor (const m of msgs.filter(m => m.type === 'human')) {\n  const match = (m.content || '').match(/\\b(\\d{11})\\b/);\n  if (match) { cpf = match[1]; break; }\n}\n\nlet nome = null, empresa = null;\nconst identResult = toolResult('validar_identidade');\nif (identResult?.status === 'ok' && identResult?.data) {\n  nome = identResult.data.nomeCompleto || null;\n  empresa = identResult.data.nomeEmpresa || null;\n}\n\nlet clinica_id = null, clinica_nome = null;\nconst clinicaResult = toolResult('buscar_detalhes_clinica');\nif (clinicaResult?.status === 'sucesso' && clinicaResult?.data) {\n  clinica_id = String(clinicaResult.data.clinica_id || '');\n  clinica_nome = clinicaResult.data.nome || null;\n}\n\nlet data_agendamento = null;\nconst aiDispMsg = [...msgs].reverse().find(m =>\n  m.type === 'ai' && Array.isArray(m.tool_calls) &&\n  m.tool_calls.some(t => t.name === 'verificar_disponibilidade')\n);\nif (aiDispMsg) {\n  const tc = aiDispMsg.tool_calls.find(t => t.name === 'verificar_disponibilidade');\n  data_agendamento = tc?.args?.data || null;\n}\n\n// Converte DD/MM/AAAA para YYYY-MM-DD\nif (data_agendamento) {\n  const parts = data_agendamento.split('/');\n  if (parts.length === 3) {\n    data_agendamento = `${parts[2]}-${parts[1].padStart(2,'0')}-${parts[0].padStart(2,'0')}`;\n  }\n}\n\nconst encerra = resultado_final !== null;\nconst sets = [`etapa_mais_alta_atingida = '${etapa}'`];\nif (cpf) sets.push(`cpf_colaborador = '${cpf}'`);\nif (nome) sets.push(`nome_colaborador = '${nome.replace(/'/g, \"''\")}'`);\nif (empresa) sets.push(`empresa = '${empresa.replace(/'/g, \"''\")}'`);\nif (clinica_id) sets.push(`clinica_id = '${clinica_id}'`);\nif (clinica_nome) sets.push(`clinica_nome = '${clinica_nome.replace(/'/g, \"''\")}'`);\nif (data_agendamento) sets.push(`data_agendamento = '${data_agendamento}'`);\nif (resultado_final) sets.push(`resultado_final = '${resultado_final}'`);\nif (houve_handoff) sets.push(`houve_handoff = true`);\nif (motivo_handoff) sets.push(`motivo_handoff = '${motivo_handoff.replace(/'/g, \"''\")}'`);\nif (encerra) {\n  sets.push(`dt_fim = NOW()`);\n  sets.push(`duracao_segundos = EXTRACT(EPOCH FROM (NOW() - dt_inicio))::INTEGER`);\n}\n\nreturn [{\n  json: {\n    status: 'ok',\n    session_id,\n    etapa,\n    evento,\n    update_sql: `UPDATE bencorp_sessoes SET ${sets.join(', ')} WHERE session_id = '${session_id}'`,\n    insert_evento_sql: `INSERT INTO bencorp_eventos (session_id, etapa, evento) SELECT '${session_id}', '${etapa}', '${evento}' WHERE NOT EXISTS (SELECT 1 FROM bencorp_eventos WHERE session_id = '${session_id}' AND etapa = '${etapa}' AND evento = '${evento}')`\n  }\n}];"
      },
      "id": "6f177e78-06d5-4029-99af-fadf95897995",
      "name": "Detectar etapa",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -208,
        -256
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT message FROM bencorp_n8n_chat_histories WHERE session_id = '{{ $json.session_id }}' ORDER BY id ASC",
        "options": {}
      },
      "id": "e03af169-5dbe-44c0-be18-bfe96f576546",
      "name": "Buscar histórico",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        -432,
        -256
      ],
      "credentials": {
        "postgres": {
          "id": "NOaCvCB1BYrOmaeO",
          "name": "Postgres account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const session_id = $('When chat message received').first().json.sessionId;\nif (!session_id) return [{ json: { status: 'skip', session_id: null } }];\nreturn [{ json: { session_id } }];\n"
      },
      "id": "bef98c93-130b-4372-ba13-36f6c86efa2f",
      "name": "Preparar session_id",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -656,
        -256
      ]
    },
    {
      "parameters": {
        "model": "openai/gpt-4o",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        -2464,
        352
      ],
      "id": "b994e683-10bd-46b8-a2f6-42d8de28afc4",
      "name": "OpenAI/gpt-4o",
      "credentials": {
        "openRouterApi": {
          "id": "3xAsu95rs6Ne4S3j",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "gpt-4o-mini",
          "mode": "list",
          "cachedResultName": "gpt-4o-mini"
        },
        "builtInTools": {},
        "options": {
          "temperature": 0.1
        }
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        -2624,
        352
      ],
      "id": "ee62335e-3e66-4c68-8d49-475da3848931",
      "name": "gpt4-4o",
      "credentials": {
        "openAiApi": {
          "id": "R8nIYEIWQIiSWpu2",
          "name": "OpenAi account"
        }
      }
    },
    {
      "parameters": {},
      "type": "@n8n/n8n-nodes-langchain.code",
      "typeVersion": 1,
      "position": [
        416,
        -32
      ],
      "id": "1cc0d336-2a89-4b01-aaaf-08ab5e8cd986",
      "name": "LangChain Code"
    }
  ],
  "pinData": {},
  "connections": {
    "When chat message received": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "handoff_para_atendente": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "gravar_aceite_lgpd": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "validar_cep": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "buscar_clinicas_por_cep": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "solicitar_atualizacao_cadastral": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "verificar_cpf_soc": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "validar_identidade": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "buscar_detalhes_clinica": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "buscar_datas_disponiveis": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "verificar_disponibilidade": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "confirmar_agendamento": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "cancelar_agendamento": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "buscar_estado_sessao": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "buscar_sessao_incompleta": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Preparar session_id",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "INSERT evento": {
      "main": [
        [
          {
            "node": "Passar output adiante",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "UPDATE sessão": {
      "main": [
        [
          {
            "node": "INSERT evento",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evento detectado?": {
      "main": [
        [
          {
            "node": "UPDATE sessão",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Passar output adiante",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "INSERT sessão": {
      "main": [
        [
          {
            "node": "Evento detectado?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Detectar etapa": {
      "main": [
        [
          {
            "node": "INSERT sessão",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar histórico": {
      "main": [
        [
          {
            "node": "Detectar etapa",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar session_id": {
      "main": [
        [
          {
            "node": "Buscar histórico",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI/gpt-4o": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 1
          }
        ]
      ]
    },
    "gpt4-4o": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": true
  },
  "versionId": "d0ef5d05-779f-4c8a-9d00-db8e4476c6c1",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "538cc04503aa1ce987b104b2ef1aac3c1b7b5f22ecf8125ad3174c4e336f48e3"
  },
  "id": "SAafOegR4CgE0wjm",
  "tags": [
    {
      "updatedAt": "2026-04-03T15:03:10.514Z",
      "createdAt": "2026-04-03T15:03:10.514Z",
      "id": "GYHN2LgePrvSrTLm",
      "name": "Observability"
    }
  ]
}