Credenciais e identidade
Como o agente se autentica no seu sistema, como sabe quem é o usuário final, e o que fazer quando só existe login e senha — com o que o modelo vê e o que ele nunca vê.
Atualizado em 01 de set. de 2026
Todo agente que faz algo útil precisa responder três perguntas diferentes — e a maior parte dos problemas de segurança vem de tratá-las como se fossem uma só:
- Quem é o sistema? A credencial da sua empresa na sua API.
- Quem é a pessoa? A identidade de quem está conversando.
- Quem pode agir? O que aquela pessoa tem direito de fazer.
Cada uma tem um lugar certo no agente. Nenhuma delas é resolvida pedindo senha no chat.
| Informação | Chega ao modelo? | Onde vive |
|---|---|---|
auth do card Webhook (token, chave, senha básica) | **Não** | Na config do nó. O runtime injeta direto na chamada HTTP. |
defaultHeaders e headers de rota | **Não** | Idem — montados na hora do request. |
baseUrl e o caminho da rota | Não (só o nome da tool) | Config do nó. |
| Parâmetros da rota | **Sim** — é o modelo que os preenche | Vêm da conversa, aparecem no evento tool_use do stream. |
| Resposta da rota | **Sim** | Volta como resultado da tool e entra no contexto. |
Variáveis do run (variables) | Só se você as colocar no prompt | Resolvidas em {{nome}} dentro do system_prompt. |
Verificado na prática
Num teste com um servidor de eco, a rota autenticada chegou ao destino com Authorization: Bearer … e o header customizado — e o evento tool_use correspondente saiu com o input vazio: nenhum pedaço da credencial passou pelo modelo nem pelo stream. É comportamento explícito do runtime, não coincidência.
A consequência prática
Tudo o que precisa entrar numa rota passa pelo modelo. Por isso um identificador opaco de cliente (acct_1042) é aceitável ali, e uma senha, um token pessoal ou um número de cartão não são.
Esta tabela evita a maior parte dos erros de quem monta o primeiro fluxo com identidade. As variáveis do variables do run não chegam sozinhas a todo lugar:
| Lugar | Resolve {{var}}? | Observação |
|---|---|---|
system_prompt do Agent | **Sim** | É o caminho normal: o valor aparece para o modelo, que o copia para a rota. |
| Condições de Se/Senão e While | **Sim** | Motor de expressões do workflow. |
| URL e headers do card **MCP** | **Sim** | Resolvidos na conexão, **sem passar pelo modelo** — é o único caminho para segredo por usuário. |
auth, baseUrl e headers do card **Webhook** | **Não** | São estáticos por nó. Um {{token}} ali viaja literal e a chamada falha. |
path, queryParams e bodyTemplate de uma rota | Só com **parâmetros da rota** | Não com variáveis do run. O valor tem que vir do modelo. |
O erro que aparece em quase todo primeiro fluxo
Declarar identifier como variável do run, usar {{identifier}} na rota — e esquecer de escrever o valor no system_prompt. Num teste real, o agente mandou identifier: "unknown": ele não tinha o valor e preencheu o campo com um palpite. A correção é uma linha no prompt: identifier: {{identifier}}.
DADOS DESTE CANAL — use exatamente estes valores nas ferramentas,
sem perguntar e sem inventar:
- channel: {{channel}}
- identifier: {{identifier}}O agente inteiro fala com a sua API usando uma credencial da empresa, configurada no card Webhook. É o padrão da maioria absoluta dos casos: atendimento, consulta de pedido, abertura de chamado, agendamento.
"auth": { "type": "bearer", "token": "sk_live_..." }
"auth": { "type": "api_key", "apiKey": "...", "apiKeyName": "X-API-Key", "apiKeyLocation": "header" }
"auth": { "type": "basic", "username": "agente", "password": "..." }| Cuidado | Por quê |
|---|---|
| Chave com **escopo mínimo** | O agente pode chamar qualquer rota habilitada no card. Dê a ele uma credencial que só faz o que as rotas fazem. |
| Chave **exclusiva do agente** | Se precisar revogar, você revoga o agente — não o seu app inteiro. E o log da sua API mostra quem foi. |
| Rotas de escrita separadas | Ver o padrão de [aprovação](/pt/api/agents/flows/orders-desk): leitura no agente de conversa, escrita só depois da trava. |
A conversa nasce dentro de um lugar onde a pessoa já está autenticada — o app, a área logada, o portal. Sua aplicação já sabe quem é: mande o identificador no variables do run.
{
"message": "cadê meu pedido?",
"variables": { "customer_id": "acct_1042" },
"stream": true
}O customer_id entra no system_prompt como {{customer_id}} e o modelo o repassa às rotas. Duas regras que fazem esse padrão ser seguro:
- O identificador é opaco — um id interno, não CPF, e-mail ou telefone.
- A sua API confere o escopo — a rota valida que aquele id pertence mesmo ao chamador, porque o valor passou pelo modelo e, em teoria, pode ser induzido.
Este é o padrão dos fluxos Suporte com chamados e Pós-venda.
Quando a chamada precisa carregar o token daquela pessoa — e não o da empresa — existe um caminho em que o segredo nunca chega ao modelo: o card MCP resolve {{variáveis}} na URL e nos headers da conexão, no momento de conectar.
{
"servers": [{
"name": "erp",
"type": "http",
"url": "https://mcp.suaempresa.com/erp",
"headers": { "X-User-Token": "{{user_token}}" }
}]
}O user_token chega no variables do run, é injetado no header da conexão e não entra no prompt nem no evento de ferramenta. O preço é que o seu sistema precisa expor um servidor MCP, não só uma API REST.
Não tente isso no card Webhook
Um {{token}} dentro de auth do Webhook não é resolvido: vai literal para o header e a chamada falha com 401. A resolução por variável existe hoje só no card MCP.
A conversa começa num canal aberto — WhatsApp, Instagram, chat do site — e ninguém autenticou nada. Este é o caso em que a pergunta "peço usuário e senha?" costuma aparecer. A resposta é vincular o canal à conta uma vez, com um código de uso único enviado ao e-mail cadastrado.
Depois do vínculo, o agente nunca mais pergunta quem é a pessoa: as rotas recebem channel + identifier (o endereço do canal, que não é segredo) e o seu back-end resolve a conta.
Identificação sem senha
O fluxo completo, com as 3 rotas de vínculo, o porteiro de PII e o que o seu back-end precisa garantir.
Não é purismo: é o caminho que o valor percorre. Uma senha digitada no chat vira uma mensagem, e uma mensagem:
| Vai parar em | Por quanto tempo |
|---|---|
| Histórico da conversa (thread) | Enquanto o thread existir — e ele existe para o cliente continuar depois. |
| Contexto do modelo | Em todas as rodadas seguintes do turno. |
| Stream do run (SSE) | Em quem estiver acompanhando a execução. |
| Logs e observabilidade | Onde quer que a mensagem seja registrada. |
E o token que a rota de login devolveria percorre o mesmo caminho de volta. Em uma frase: a conversa não é um cofre e não deve virar um.
| Situação | O que fazer |
|---|---|
| A pessoa já está logada no seu app | Padrão 2: mande o id no variables. O seu back-end usa a credencial de serviço para falar com o legado. |
| Canal aberto, sem login | Padrão 4: vínculo por código. Melhor que senha, inclusive em segurança — não existe segredo reutilizável trafegando. |
| Precisa mesmo agir *como* aquele usuário no legado | O seu back-end faz o login (server-to-server), guarda a sessão e expõe uma rota por channel/identifier. O agente nunca vê a credencial. |
| Precisa de token pessoal no header da chamada | Padrão 3, com MCP — a única forma em que o segredo não passa pelo modelo. |
Se ainda assim for inevitável
Se um caso específico obrigar a coletar credencial na conversa, trate como incidente controlado: canal privado, credencial de uso único, expiração em minutos, troca imediata por um token curto do seu lado, e nunca repetir o valor na resposta. Prefira, honestamente, redesenhar o fluxo.
O cliente vai mandar o que não deveria — CPF, foto do cartão, senha — porque é o que ele faz há vinte anos com atendimento humano. O card Guardrails inspeciona a mensagem antes do agente e desvia por uma saída própria.
{
"target": "input",
"checks": {
"pii": true,
"blocklist": ["senha", "cartão de crédito"],
"jailbreak": false,
"moderation": false,
"custom": ""
},
"fail_message": "A mensagem parece conter um dado sensível."
}| Checagem | Como funciona | Custo |
|---|---|---|
pii | Regex determinístico para CPF, CNPJ, cartão (com validação de Luhn), e-mail e telefone. | Zero |
blocklist | Termos ou expressões proibidas. | Zero |
jailbreak · moderation · custom | Classificação por modelo, as três numa única chamada. | Uma inferência por mensagem |
As saídas pass e fail funcionam como as do Se/Senão: no caminho fail, um agente sem nenhuma ferramenta pede gentilmente para a pessoa não enviar aquele dado — sem repetir o valor. É o desenho do fluxo Identificação sem senha.
O agente pode chamar qualquer rota habilitada. A chave dele deve poder fazer exatamente isso — nada além. E deve ser exclusiva, para poder ser revogada sozinha.
O escopo é por slot: cada agente só enxerga os cards ligados a ele. Ligue o Webhook de escrita apenas no agente executor — e, quando dois agentes dividem o mesmo card, use tools_filter para cada um expor só o seu subconjunto.
O valor passou pelo modelo. Trate como entrada de usuário: a rota valida que aquele id pertence ao chamador.
Parâmetro de rota é preenchido pelo modelo e aparece no stream. Se algum campo assim existe, o desenho está errado — volte aos padrões 3 e 4.
Parâmetro opcional que o modelo não preenche viaja literalmente como {{nome}} na query string, no caminho ou no corpo. Declare required: true e diga no texto qual valor usar quando não houver.
A checagem pii custa zero e evita que documento e cartão entrem no histórico da conversa.
O erro volta ao modelo como texto e ele improvisa uma resposta. Escreva no prompt o que fazer quando a ferramenta falhar — normalmente: dizer a verdade e oferecer outro caminho.
Fluxos prontos
Os quatro padrões acima montados, com JSON para importar e testar.

