Ir para o conteúdo

Identificação sem senha

O agente descobre quem é o cliente por vínculo de canal com código de uso único — nenhuma senha passa pela conversa — e só então consulta plano e faturas.

Atualizado em 01 de set. de 2026

A pergunta que aparece em toda implantação: "e quando o cliente precisa se identificar? Peço usuário e senha?"

Não. Senha digitada no chat vira mensagem — e mensagem vai para o histórico da conversa, para o contexto do modelo e para o stream de quem acompanha o run. Uma vez lá, não sai.

O que se faz no lugar é o que este fluxo mostra: vincular o canal à conta uma única vez, com um código de uso único enviado ao e-mail cadastrado. Depois disso, o próprio sistema sabe de quem é aquele WhatsApp — e o agente nunca precisa perguntar quem é a pessoa, nem manipular um id de conta.

A propriedade que torna isto seguro

As rotas de dado pessoal (meu_plano, minhas_faturas) não recebem id de conta. Elas recebem channel + identifier — o endereço do canal, que não é segredo — e é o seu back-end que resolve de quem é a conta a partir do vínculo. Assim, mesmo que o modelo erre ou alguém tente induzi-lo, não existe id de outra pessoa para ele passar.

Como importar

Na plataforma: Agents → Importar, escolha o arquivo baixado. Ele entra como um rascunho novo (nada existente é alterado), com ids de observabilidade renovados. Depois preencha o que é do seu ambiente — credenciais, URLs e bases de conhecimento — e publique.

O arquivo

account-link.hinow-agent.jsonjson
{
 "format": "hinow.agent",
 "version": 1,
 "exported_at": "2026-09-01T00:00:00Z",
 "credentials_included": false,
 "agent": {
  "name": "Identificação sem senha",
  "description": "O agente descobre quem é o cliente por vínculo de canal com código de uso único — nenhuma senha passa pela conversa — e só então consulta plano e faturas.",
  "avatar": null,
  "model": null,
  "system_prompt": null,
  "config": {},
  "tools": null,
  "workflow": {
   "nodes": [
    {
     "id": "start",
     "type": "start",
     "position": {
      "x": 300,
      "y": 0
     },
     "data": {
      "label": "Início",
      "variables": [
       {
        "name": "channel",
        "type": "input",
        "required": true,
        "description": "Canal em que a conversa chegou: whatsapp, webchat ou email."
       },
       {
        "name": "identifier",
        "type": "input",
        "required": true,
        "description": "Identificador da pessoa neste canal (telefone, id de sessão). Não é segredo: é só o endereço do canal."
       }
      ]
     }
    },
    {
     "id": "guard-1",
     "type": "guardrails",
     "position": {
      "x": 288,
      "y": 150
     },
     "data": {
      "name": "Porteiro",
      "config": {
       "target": "input",
       "checks": {
        "pii": true
       },
       "fail_message": "A mensagem parece conter um dado sensível (documento, cartão ou telefone completo)."
      }
     }
    },
    {
     "id": "agent-1",
     "type": "agent",
     "position": {
      "x": 295,
      "y": 330
     },
     "data": {
      "name": "conta",
      "model": "hinow/himax",
      "system_prompt": "Você é o assistente da conta do cliente na Vega Telecom, atendendo pelo canal em que a pessoa escreveu.\n\nDADOS DESTE CANAL — use exatamente estes valores nas ferramentas, sem perguntar e sem inventar:\n- channel: {{channel}}\n- identifier: {{identifier}}\n\nIDENTIDADE — LEIA ANTES DE QUALQUER COISA\nVocê NÃO sabe quem é a pessoa até o sistema dizer. E você NUNCA pede senha, token, número de cartão ou documento — nem para \"confirmar identidade\". Se a pessoa mandar algo assim por conta própria, peça para não repetir e siga sem usar o valor.\n\nO caminho correto é sempre este:\n1. verificar_canal — descobre se este canal já está ligado a uma conta. Faça isso ANTES de qualquer consulta de dado pessoal.\n2. Se já estiver ligado: siga normalmente, chamando a pessoa pelo nome.\n3. Se não estiver: peça só o E-MAIL da conta e chame solicitar_codigo. Explique que vai chegar um código de 6 dígitos no e-mail.\n4. Quando a pessoa disser o código, chame confirmar_codigo. Se der certo, o canal fica ligado para sempre — nunca mais peça código.\n5. Se o código falhar, diga o motivo (errado, expirado) e ofereça pedir outro. Depois de 3 tentativas sem sucesso, ofereça atendimento humano.\n\nRepare: você nunca informa o id da conta para as ferramentas de dado pessoal. É o sistema que resolve a conta a partir do canal ligado — e é por isso que este desenho é seguro.\n\nO QUE VOCÊ RESPONDE\n- Fatura, consumo, plano e status do serviço: sempre pelas ferramentas, com o valor exato.\n- Como funciona o serviço, prazos, regras e procedimentos: pela base de conhecimento.\n- Quando não souber: diga que não sabe e ofereça abrir atendimento humano.\n\nTOM\nCurto, claro, sem jargão. Uma pergunta de cada vez.",
      "config": {
       "temperature": 0.3,
       "max_tool_loops": 12
      }
     }
    },
    {
     "id": "agent-pii",
     "type": "agent",
     "position": {
      "x": 55,
      "y": 330
     },
     "data": {
      "name": "aviso",
      "model": "hinow/himax",
      "system_prompt": "A mensagem do cliente traz um dado sensível (documento, cartão ou telefone completo). Peça, com gentileza e em uma frase, que ele não envie esse tipo de dado por aqui, explique que não é necessário para o atendimento, e convide a continuar com a dúvida em palavras. Não repita o valor recebido.",
      "config": {
       "temperature": 0.4,
       "tools_filter": []
      }
     }
    },
    {
     "id": "rag-1",
     "type": "rag_search",
     "position": {
      "x": 575,
      "y": 260
     },
     "data": {
      "label": "Base da operadora",
      "config": {
       "rag_ids": [],
       "top_k": 4,
       "min_score": 0.35
      }
     }
    },
    {
     "id": "hook-1",
     "type": "webhook",
     "position": {
      "x": 575,
      "y": 405
     },
     "data": {
      "label": "API de contas",
      "config": {
       "name": "Contas Vega",
       "baseUrl": "https://api.suaempresa.com/accounts/v1",
       "timeout": 15000,
       "auth": {
        "type": "bearer",
        "token": "COLE_SEU_TOKEN_AQUI"
       },
       "retryOnError": true,
       "maxRetries": 2,
       "routes": [
        {
         "id": "r-verificar_canal",
         "name": "verificar_canal",
         "method": "GET",
         "path": "/identity/channel",
         "description": "Diz se este canal (WhatsApp, e-mail, chat) já está ligado a uma conta e, se estiver, o primeiro nome do titular.",
         "whenToUse": "SEMPRE no início da conversa, antes de qualquer consulta de dado pessoal.",
         "responseDescription": "Se está ligado, o primeiro nome do titular; se não, a indicação de que precisa vincular.",
         "parameters": [
          {
           "name": "channel",
           "type": "string",
           "required": true,
           "description": "Canal desta conversa: whatsapp, webchat ou email",
           "howToObtain": "É o canal em que a mensagem chegou",
           "example": "whatsapp"
          },
          {
           "name": "identifier",
           "type": "string",
           "required": true,
           "description": "Identificador da pessoa neste canal",
           "howToObtain": "No WhatsApp é o telefone de quem escreve; no webchat é o id da sessão",
           "example": "5511987654321"
          }
         ],
         "enabled": true,
         "queryParams": {
          "channel": "{{channel}}",
          "identifier": "{{identifier}}"
         }
        },
        {
         "id": "r-solicitar_codigo",
         "name": "solicitar_codigo",
         "method": "POST",
         "path": "/identity/link/request",
         "description": "Envia um código de 6 dígitos para o e-mail cadastrado na conta, para ligar este canal a ela.",
         "whenToUse": "Quando verificar_canal disser que o canal não está ligado e a pessoa informar o e-mail da conta. Nunca peça senha no lugar disso.",
         "responseDescription": "Confirmação de envio (sem revelar o e-mail completo) ou aviso de que o e-mail não corresponde a nenhuma conta.",
         "parameters": [
          {
           "name": "email",
           "type": "string",
           "required": true,
           "description": "E-mail cadastrado na conta",
           "howToObtain": "Pergunte à pessoa qual o e-mail da conta",
           "example": "cliente@exemplo.com"
          },
          {
           "name": "channel",
           "type": "string",
           "required": true,
           "description": "Canal desta conversa",
           "howToObtain": "O mesmo de verificar_canal",
           "example": "whatsapp"
          },
          {
           "name": "identifier",
           "type": "string",
           "required": true,
           "description": "Identificador neste canal",
           "howToObtain": "O mesmo de verificar_canal",
           "example": "5511987654321"
          }
         ],
         "enabled": true,
         "bodyTemplate": "{\"email\": \"{{email}}\", \"channel\": \"{{channel}}\", \"identifier\": \"{{identifier}}\"}"
        },
        {
         "id": "r-confirmar_codigo",
         "name": "confirmar_codigo",
         "method": "POST",
         "path": "/identity/link/confirm",
         "description": "Confere o código recebido por e-mail e liga este canal à conta em definitivo.",
         "whenToUse": "Logo depois que a pessoa disser o código. Se der certo, nunca mais peça código neste canal.",
         "responseDescription": "Se o canal foi ligado e o nome do titular, ou o motivo da recusa (código errado, expirado, sem pedido).",
         "parameters": [
          {
           "name": "code",
           "type": "string",
           "required": true,
           "description": "Código de 6 dígitos",
           "howToObtain": "A pessoa dita na conversa",
           "example": "482913"
          },
          {
           "name": "channel",
           "type": "string",
           "required": true,
           "description": "Canal desta conversa",
           "howToObtain": "O mesmo de solicitar_codigo",
           "example": "whatsapp"
          },
          {
           "name": "identifier",
           "type": "string",
           "required": true,
           "description": "Identificador neste canal",
           "howToObtain": "O mesmo de solicitar_codigo",
           "example": "5511987654321"
          }
         ],
         "enabled": true,
         "bodyTemplate": "{\"code\": \"{{code}}\", \"channel\": \"{{channel}}\", \"identifier\": \"{{identifier}}\"}"
        },
        {
         "id": "r-meu_plano",
         "name": "meu_plano",
         "method": "GET",
         "path": "/account/plan",
         "description": "Plano contratado, velocidade, franquia e valor mensal do titular deste canal.",
         "whenToUse": "Depois que o canal estiver ligado, quando a pergunta for sobre plano, pacote ou mensalidade.",
         "responseDescription": "Nome do plano, valor, benefícios e data de renovação.",
         "parameters": [
          {
           "name": "channel",
           "type": "string",
           "required": true,
           "description": "Canal desta conversa",
           "howToObtain": "O mesmo de verificar_canal",
           "example": "whatsapp"
          },
          {
           "name": "identifier",
           "type": "string",
           "required": true,
           "description": "Identificador neste canal",
           "howToObtain": "O mesmo de verificar_canal",
           "example": "5511987654321"
          }
         ],
         "enabled": true,
         "queryParams": {
          "channel": "{{channel}}",
          "identifier": "{{identifier}}"
         }
        },
        {
         "id": "r-minhas_faturas",
         "name": "minhas_faturas",
         "method": "GET",
         "path": "/account/invoices",
         "description": "Últimas faturas do titular deste canal, com status de pagamento.",
         "whenToUse": "Depois que o canal estiver ligado, quando a pergunta for sobre fatura, boleto, vencimento ou pagamento.",
         "responseDescription": "Lista com competência, valor, vencimento, status e link da segunda via.",
         "parameters": [
          {
           "name": "channel",
           "type": "string",
           "required": true,
           "description": "Canal desta conversa",
           "howToObtain": "O mesmo de verificar_canal",
           "example": "whatsapp"
          },
          {
           "name": "identifier",
           "type": "string",
           "required": true,
           "description": "Identificador neste canal",
           "howToObtain": "O mesmo de verificar_canal",
           "example": "5511987654321"
          },
          {
           "name": "limite",
           "type": "number",
           "required": true,
           "description": "Quantas faturas trazer. Use 3 quando a pessoa não especificar — nunca deixe em branco",
           "example": "3"
          }
         ],
         "enabled": true,
         "queryParams": {
          "channel": "{{channel}}",
          "identifier": "{{identifier}}",
          "limite": "{{limite}}"
         }
        }
       ]
      }
     }
    },
    {
     "id": "end-1",
     "type": "end",
     "position": {
      "x": 305,
      "y": 510
     },
     "data": {
      "label": "Fim",
      "status": "success"
     }
    }
   ],
   "edges": [
    {
     "id": "e-start-guard-1-d",
     "source": "start",
     "target": "guard-1"
    },
    {
     "id": "e-guard-1-agent-1-pass",
     "source": "guard-1",
     "target": "agent-1",
     "sourceHandle": "pass"
    },
    {
     "id": "e-guard-1-agent-pii-fail",
     "source": "guard-1",
     "target": "agent-pii",
     "sourceHandle": "fail"
    },
    {
     "id": "e-rag-1-agent-1-slot-1",
     "source": "rag-1",
     "target": "agent-1",
     "targetHandle": "slot-1"
    },
    {
     "id": "e-hook-1-agent-1-tool",
     "source": "hook-1",
     "target": "agent-1",
     "sourceHandle": "tool",
     "targetHandle": "slot-2"
    },
    {
     "id": "e-agent-1-end-1-d",
     "source": "agent-1",
     "target": "end-1"
    },
    {
     "id": "e-agent-pii-end-1-d",
     "source": "agent-pii",
     "target": "end-1"
    }
   ]
  }
 }
}

Card a card

CardPor que está aquiO que faz
**Início**Traz o endereço do canal.channel e identifier chegam no variables do run. Não são segredo — são o telefone/sessão de quem escreveu.
**Porteiro (guardrails)**Rede de proteção.Checagem pii (determinística, sem custo): CPF, CNPJ, cartão, e-mail e telefone. Saída fail desvia antes de o agente ver.
**Agent aviso**O caminho do dado sensível.Pede gentilmente para não enviar aquilo, sem repetir o valor. tools_filter: []: sem nenhuma ferramenta.
**Agent conta**O atendimento de verdade.Segue o roteiro de identidade: verificar canal → (se preciso) código → consultar. Nunca pede senha.
**Webhook** (5 rotas)Identidade e dados da conta.3 rotas de vínculo + 2 de consulta. Todas por channel/identifier.
**Conhecimento (RAG)**O que não é pessoal.Como funciona o serviço, prazos, procedimentos. Não depende de saber quem é a pessoa.

O roteiro de identidade

  1. 1

    `verificar_canal`

    Sempre primeiro. Diz se este WhatsApp já está ligado a uma conta e o primeiro nome do titular.

  2. 2

    Já vinculado? Segue a vida

    O agente chama pelo nome e responde. Nenhuma pergunta de identidade.

  3. 3

    Não vinculado: pede só o e-mail

    solicitar_codigo manda um código de 6 dígitos para o e-mail cadastrado — quem não tem acesso ao e-mail não avança.

  4. 4

    `confirmar_codigo`

    A pessoa dita o código, o canal fica ligado em definitivo. Nunca mais se pede código nesse canal.

  5. 5

    Consulta liberada

    meu_plano e minhas_faturas respondem pelo titular do canal — sem id de conta trafegando.

Três coisas que o seu back-end precisa fazer

1. Expirar o código (5–10 minutos) e limitar tentativas. 2. Nunca revelar o e-mail completo na resposta de solicitar_codigo (c***@exemplo.com) — senão a rota vira ferramenta de descoberta de e-mail. 3. Responder igual para e-mail que existe e que não existe, pelo mesmo motivo.

E se o meu sistema só tem login e senha?

Aí a troca acontece fora da conversa, não dentro dela:

  • A sua aplicação já autenticou a pessoa (app, área logada, portal): ela manda o identificador no variables do run e você nem precisa deste fluxo — é o padrão do Suporte com chamados.
  • A conversa começa em canal aberto (WhatsApp, Instagram): use este fluxo. O código de uso único substitui a senha com segurança melhor, não pior.
  • Precisa mesmo de credencial por usuário na chamada HTTP: existe um caminho em que o segredo não passa pelo modelo — o card MCP resolve {{variáveis}} nos headers da conexão. Está explicado em Credenciais e identidade.

O que nunca funciona bem é pedir a senha no chat e mandá-la numa rota de login: o valor fica no histórico, o token de volta também, e você acabou de transformar uma conversa num cofre.

Como adaptar ao seu caso

  • Outro canal: channel aceita o que você quiser (whatsapp, webchat, instagram, email) — o vínculo é por par canal+identificador.
  • Outro segundo fator: SMS no lugar do e-mail, se o cadastro tiver telefone verificado. O desenho não muda.
  • Mais consultas: cada dado do titular vira uma rota nova por channel/identifier. Nunca por id de conta vindo do modelo.
  • Guardrails mais rígido: blocklist para termos proibidos e jailbreak/moderation (esses custam uma classificação por mensagem).

Todos os fluxos prontos

Outros agents completos para importar e adaptar.