Para criar uma boa UX de verificação de CPF em chatbots, extraia os dígitos de qualquer formato de texto livre, confirme o número com mascaramento parcial antes de consultar a API e limite as tentativas a três antes de escalar para atendimento humano — esse fluxo reduz abandono e protege a privacidade do usuário.
Introdução
Chatbots e interfaces conversacionais são cada vez mais utilizados para atendimento ao cliente, vendas e onboarding. No Brasil, onde o CPF é requisito para inúmeras operações, é comum que o chatbot precise solicitar e verificar esse dado durante a conversa.
Coletar um CPF em uma interface conversacional é fundamentalmente diferente de um formulário web. Não há campo de input com máscara, não há validação visual em tempo real e o usuário pode responder de formas imprevisíveis. Projetar bem esse fluxo faz diferença direta na taxa de conclusão do onboarding.
Desafios de coletar CPF em chatbots
Formatos variados de resposta
O usuário pode responder o CPF de diversas formas:
12345678901(apenas dígitos)123.456.789-01(formatado)123 456 789 01(com espaços)meu cpf é 123.456.789-01(com texto)CPF: 123.456.789-01(com prefixo)
O chatbot precisa extrair os dígitos de qualquer uma dessas variações.
Privacidade em canais públicos
Em plataformas como WhatsApp e Telegram, a conversa pode ser vista por terceiros em dispositivos compartilhados. O chatbot deve tratar o CPF com cuidado, mascarando-o em confirmações.
Fluxo não linear
Diferente de um formulário, o usuário pode mudar de assunto, fazer perguntas ou se confundir no meio do fluxo. O chatbot precisa lidar com interrupções de forma graciosa.
Extração de CPF de texto livre
A primeira função essencial é extrair o CPF de qualquer formato de mensagem.
function extrairCPF(mensagem) {
// Remover tudo exceto digitos, pontos, tracos e espacos
const limpo = mensagem.replace(/[^\d.\-\s]/g, '');
// Tentar extrair 11 digitos consecutivos (com ou sem formatacao)
const padroes = [
/(\d{3})[.\s]?(\d{3})[.\s]?(\d{3})[-.\s]?(\d{2})/,
/(\d{11})/
];
for (const padrao of padroes) {
const match = limpo.match(padrao);
if (match) {
const digits = match[0].replace(/\D/g, '');
if (digits.length === 11) return digits;
}
}
return null;
}
// Testes:
// extrairCPF('12345678901') => '12345678901'
// extrairCPF('123.456.789-01') => '12345678901'
// extrairCPF('meu cpf e 123.456.789-01') => '12345678901'
// extrairCPF('CPF: 123 456 789 01') => '12345678901'
// extrairCPF('ola, tudo bem?') => null
Máquina de estados do fluxo conversacional
O fluxo de verificação de CPF no chatbot pode ser modelado como uma máquina de estados finitos.
class FluxoCPF {
constructor(apiKey) {
this.apiKey = apiKey;
this.estado = 'AGUARDANDO_CPF';
this.tentativas = 0;
this.maxTentativas = 3;
this.cpfExtraido = null;
this.dadosCPF = null;
}
async processarMensagem(mensagem) {
switch (this.estado) {
case 'AGUARDANDO_CPF':
return this.handleAguardandoCPF(mensagem);
case 'CONFIRMANDO_CPF':
return this.handleConfirmandoCPF(mensagem);
case 'CONCLUIDO':
return { texto: 'Seu CPF ja foi verificado. Como posso ajudar?', fim: true };
case 'FALHA':
return { texto: 'Nao consegui verificar seu CPF. Deseja tentar novamente?', fim: false };
default:
return { texto: 'Desculpe, algo deu errado. Vamos comecar de novo.', fim: false };
}
}
async handleAguardandoCPF(mensagem) {
const cpf = extrairCPF(mensagem);
if (!cpf) {
this.tentativas++;
if (this.tentativas >= this.maxTentativas) {
this.estado = 'FALHA';
return {
texto: 'Nao consegui identificar um CPF na sua mensagem. ' +
'Por favor, entre em contato com nosso atendimento humano.',
fim: true
};
}
return {
texto: 'Nao encontrei um CPF na sua mensagem. ' +
'Por favor, envie apenas os 11 digitos do seu CPF.',
fim: false
};
}
if (!validarDigitosCPF(cpf)) {
return {
texto: 'O CPF informado parece estar incorreto. ' +
'Verifique os numeros e envie novamente.',
fim: false
};
}
this.cpfExtraido = cpf;
const mascarado = `${cpf.slice(0, 3)}.***.***-${cpf.slice(9)}`;
this.estado = 'CONFIRMANDO_CPF';
return {
texto: `Identifiquei o CPF ${mascarado}. Esta correto? Responda "sim" ou "nao".`,
fim: false
};
}
async handleConfirmandoCPF(mensagem) {
const resposta = mensagem.toLowerCase().trim();
if (resposta === 'nao' || resposta === 'não' || resposta === 'n') {
this.estado = 'AGUARDANDO_CPF';
this.cpfExtraido = null;
this.tentativas = 0;
return {
texto: 'Sem problema. Por favor, envie o CPF correto.',
fim: false
};
}
if (resposta === 'sim' || resposta === 's' || resposta === 'yes') {
return this.consultarAPI();
}
return {
texto: 'Nao entendi. O CPF esta correto? Responda "sim" ou "nao".',
fim: false
};
}
async consultarAPI() {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
const res = await fetch(`https://api.cpfhub.io/cpf/${this.cpfExtraido}`, {
headers: {
'x-api-key': this.apiKey,
'Accept': 'application/json'
},
signal: controller.signal
});
clearTimeout(timeoutId);
const json = await res.json();
if (json.success) {
this.dadosCPF = json.data;
this.estado = 'CONCLUIDO';
const primeiroNome = json.data.name.split(' ')[0];
return {
texto: `CPF verificado com sucesso! Ola, ${primeiroNome}. Como posso ajudar?`,
dados: json.data,
fim: true
};
} else {
this.estado = 'AGUARDANDO_CPF';
return {
texto: 'O CPF informado nao foi encontrado na base. ' +
'Verifique os numeros e envie novamente.',
fim: false
};
}
} catch (err) {
clearTimeout(timeoutId);
this.estado = 'AGUARDANDO_CPF';
return {
texto: 'Estou com dificuldade para verificar seu CPF no momento. ' +
'Pode tentar novamente em alguns instantes?',
fim: false
};
}
}
}
function validarDigitosCPF(cpf) {
if (cpf.length !== 11 || /^(\d)\1{10}$/.test(cpf)) return false;
let soma = 0;
for (let i = 0; i < 9; i++) soma += parseInt(cpf[i]) * (10 - i);
let resto = (soma * 10) % 11;
if (resto === 10) resto = 0;
if (resto !== parseInt(cpf[9])) return false;
soma = 0;
for (let i = 0; i < 10; i++) soma += parseInt(cpf[i]) * (11 - i);
resto = (soma * 10) % 11;
if (resto === 10) resto = 0;
return resto === parseInt(cpf[10]);
}
Interface de chat com widget de CPF
Para melhorar a experiência, o chatbot pode exibir um widget dedicado de input de CPF em vez de depender apenas de texto livre.
<div class="chat-container" id="chatContainer">
<div class="chat-messages" id="chatMessages"></div>
<div class="chat-input-area" id="inputArea">
<input type="text" id="chatInput" placeholder="Digite sua mensagem..." />
<button onclick="enviarMensagem()">Enviar</button>
</div>
<div class="cpf-widget" id="cpfWidget" style="display: none;">
<p>Digite seu CPF:</p>
<input
type="text"
id="cpfWidgetInput"
inputmode="numeric"
placeholder="000.000.000-00"
maxlength="14"
/>
<button onclick="enviarCPFWidget()">Verificar</button>
<button class="btn-secondary" onclick="fecharWidget()">Digitar no chat</button>
</div>
</div>
<style>
.chat-container {
max-width: 420px;
margin: 20px auto;
border: 1px solid #ddd;
border-radius: 12px;
overflow: hidden;
display: flex;
flex-direction: column;
height: 600px;
}
.chat-messages {
flex: 1;
padding: 16px;
overflow-y: auto;
background: #f8f9fa;
}
.msg {
max-width: 80%;
padding: 10px 14px;
border-radius: 12px;
margin-bottom: 8px;
font-size: 0.95rem;
line-height: 1.4;
}
.msg-bot {
background: #fff;
border: 1px solid #e0e0e0;
align-self: flex-start;
}
.msg-user {
background: #3498db;
color: #fff;
margin-left: auto;
}
.chat-input-area {
display: flex;
padding: 12px;
border-top: 1px solid #ddd;
gap: 8px;
}
.chat-input-area input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 8px;
outline: none;
}
.chat-input-area button {
padding: 10px 16px;
background: #3498db;
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
}
.cpf-widget {
padding: 16px;
background: #eef6ff;
border-top: 1px solid #b3d4fc;
text-align: center;
}
.cpf-widget input {
display: block;
width: 100%;
padding: 12px;
font-size: 1.2rem;
text-align: center;
letter-spacing: 2px;
border: 2px solid #3498db;
border-radius: 8px;
margin: 8px 0;
outline: none;
}
.cpf-widget button {
padding: 10px 20px;
border: none;
border-radius: 8px;
cursor: pointer;
margin: 4px;
}
.cpf-widget button:first-of-type {
background: #3498db;
color: #fff;
}
.btn-secondary {
background: transparent;
color: #666;
text-decoration: underline;
}
</style>
Boas práticas de UX conversacional para CPF
1. Solicite o CPF com contexto
Em vez de simplesmente perguntar "Qual é o seu CPF?", explique por que precisa do dado:
- "Para localizar seu pedido, preciso do seu CPF. Pode me informar?"
- "Vou verificar seu cadastro. Qual o CPF do titular?"
2. Confirme antes de consultar
Sempre confirme o CPF com o usuário antes de fazer a consulta na API. Exiba o CPF parcialmente mascarado para proteger a privacidade.
3. Limite as tentativas
Após 3 tentativas falhas, direcione para atendimento humano. Insistir além disso gera frustração e aumenta o abandono do fluxo.
4. Aceite voz em chatbots de voz
Para interfaces de voz (IVR, Alexa, Google Assistant), aceite o CPF dígito por dígito e repita para confirmação: "Entendi 1-2-3-4-5-6-7-8-9-0-1. Está correto?"
5. Não armazene no histórico
Em plataformas como WhatsApp Business, o CPF fica visível no histórico de conversas. Oriente o usuário a apagar a mensagem após a verificação ou use input widgets que não ficam no histórico. As diretrizes de privacidade da ANPD recomendam minimizar a exposição de dados pessoais em canais de comunicação.
Integração com WhatsApp Business API
Um exemplo simplificado de webhook para processar mensagens do WhatsApp com verificação de CPF.
// webhook-whatsapp.js (Node.js/Express)
const express = require('express');
const app = express();
app.use(express.json());
const sessoes = new Map();
app.post('/webhook', async (req, res) => {
const { from, body } = req.body.message;
if (!sessoes.has(from)) {
sessoes.set(from, new FluxoCPF(process.env.CPFHUB_API_KEY));
}
const fluxo = sessoes.get(from);
const resultado = await fluxo.processarMensagem(body);
// Enviar resposta via WhatsApp API
await enviarMensagemWhatsApp(from, resultado.texto);
if (resultado.fim) {
// Limpar sessao apos 5 minutos
setTimeout(() => sessoes.delete(from), 5 * 60 * 1000);
}
res.sendStatus(200);
});
async function enviarMensagemWhatsApp(para, texto) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
await fetch('https://graph.facebook.com/v18.0/PHONE_ID/messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.WHATSAPP_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messaging_product: 'whatsapp',
to: para,
text: { body: texto }
}),
signal: controller.signal
});
clearTimeout(timeoutId);
} catch (err) {
clearTimeout(timeoutId);
console.error('Erro ao enviar mensagem:', err);
}
}
app.listen(3000);
Métricas para chatbots com verificação de CPF
Monitore as seguintes métricas para avaliar a eficácia do fluxo:
- Taxa de extração bem-sucedida — percentual de mensagens em que o CPF foi identificado corretamente na primeira tentativa.
- Número médio de turnos — quantas mensagens são trocadas até a verificação ser concluída. O ideal é 3 (pedido + resposta + confirmação).
- Taxa de fallback para atendimento humano — quantos usuários não conseguem completar o fluxo e são encaminhados a um atendente.
- Tempo total do fluxo — desde a solicitação do CPF até a verificação concluída.
Perguntas frequentes
Como extrair o CPF de mensagens em formato livre no chatbot?
Use uma função que remove tudo exceto dígitos, pontos, traços e espaços da mensagem, depois tenta casar os 11 dígitos com um padrão regex. Isso cobre formatos com e sem pontuação, com prefixos como "CPF:" e até com texto ao redor. Se a extração falhar após 3 tentativas, escale para atendimento humano.
Qual o impacto da latência da API na experiência conversacional?
A API CPFHub.io responde em aproximadamente 900ms. Para chatbots, isso é confortável: o bot pode exibir uma mensagem de "aguarde" enquanto a consulta é processada. Configure o timeout do seu servidor para 10 segundos — suficiente para acomodar variações de rede sem travar a sessão do usuário.
Como lidar com o CPF do usuário em canais públicos como WhatsApp?
Exiba o CPF parcialmente mascarado na confirmação (ex: 123.***.***-01), nunca repita o número completo em texto. Oriente o usuário a apagar a mensagem após a verificação. Para máxima privacidade, use widgets de input dedicados que não ficam no histórico de chat, quando a plataforma suportar.
A verificação de CPF em chatbots é compatível com a LGPD?
Sim, desde que você informe ao usuário a finalidade da coleta antes de pedir o CPF, não armazene o dado além do necessário para a operação, e documente a base legal para o tratamento. O consentimento pode ser obtido no início do fluxo conversacional com uma mensagem clara antes de solicitar o número.
Conclusão
A verificação de CPF em chatbots exige uma abordagem diferente da web tradicional. A extração de CPF de texto livre, a confirmação com mascaramento, o limite de tentativas e a escalação para atendimento humano são elementos essenciais para uma boa experiência conversacional — e para manter as taxas de conclusão do fluxo em níveis saudáveis.
A API da CPFHub.io se encaixa diretamente nesse fluxo: responde em ~900ms, retorna nome e data de nascimento do titular e não bloqueia ao atingir o limite do plano — cobra apenas R$0,15 por consulta extra. Comece com 50 consultas gratuitas por mês, sem cartão de crédito, em cpfhub.io.
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



