Introdução
Para verificar a identidade de uma pessoa com documento e selfie via API, você cria um link de verificação com uma chamada POST, envia a URL para a pessoa e recebe a decisão (approved, manual_review ou rejected) em um webhook assinado. A captura da selfie, a leitura do documento e a comparação facial acontecem em uma página hospedada pela CPFHub.io, então você não precisa construir nem manter essa interface.
A Verificação de Identidade da CPFHub.io faz a prova de vida, lê o documento (CNH, RG ou passaporte), compara o rosto da selfie com a foto do documento e cruza os dados com o CPF. Ela está liberada em todos os planos, inclusive no Grátis.
Neste tutorial, vamos criar o link, entender a decisão devolvida, validar o webhook com segurança e organizar o fluxo de revisão manual. Os exemplos usam os campos da documentação oficial.
Como o fluxo funciona
O fluxo tem três passos e o seu backend participa de dois deles:
POST /kyc/linkscria o link e devolve aurl.- Você envia a
urlpara a pessoa ou redireciona para ela no seu cadastro. - A decisão chega no seu webhook, ou pode ser consultada com
GET /kyc/links/{id}.
-
Autenticação: o header
x-api-key, a mesma chave das consultas de CPF. -
Custo: 5 créditos por verificação concluída, do mesmo saldo das consultas. Criar e consultar links não custa nada, e falha técnica da CPFHub.io não cobra.
-
O que não faz: a verificação não valida o documento em base do governo, não faz perícia documental e não aceita CNH Digital.
Passo 1: criar o link de verificação
O único cabeçalho obrigatório é x-api-key. Os campos do body são todos opcionais:
| Campo | Para que serve |
|---|---|
externalId | O seu identificador do cadastro (até 200 caracteres). Volta no webhook e permite ligar a decisão ao usuário certo. |
redirectUrl | Para onde a pessoa volta ao terminar. Só https. |
expiresInMinutes | Validade, de 5 a 10080. Padrão: 1440 (24 h). |
maxAttempts | Tentativas, de 1 a 10. Padrão: 3. |
Exemplo com cURL
curl -X POST "https://api.cpfhub.io/kyc/links" \
--max-time 30 \
-H "x-api-key: SUA_CHAVE_DE_API" \
-H "content-type: application/json" \
-d '{
"externalId": "cliente-4821",
"redirectUrl": "https://seusite.com.br/cadastro/concluido"
}'
Exemplo em TypeScript com timeout
const res = await fetch('https://api.cpfhub.io/kyc/links', {
method: 'POST',
headers: {
'x-api-key': process.env.CPFHUB_API_KEY!,
'content-type': 'application/json',
},
body: JSON.stringify({
externalId: 'cliente-4821',
redirectUrl: 'https://seusite.com.br/cadastro/concluido',
}),
signal: AbortSignal.timeout(30_000),
})
const body = await res.json()
if (!res.ok) {
// error vem como string ou como objeto { message }
const message = typeof body.error === 'string' ? body.error : body.error?.message
throw new Error(`CPFHub.io ${res.status}: ${message}`)
}
console.log(body.data.url) // envie para a pessoa
Resposta
{
"success": true,
"data": {
"id": "5b1d7c2e-8a4f-4e7b-9c3d-2f6a1e0b9d47",
"url": "https://verificar.cpfhub.io/AbC123xYz",
"externalId": "cliente-4821",
"expiresAt": "2026-11-06T14:00:00.000Z",
"maxAttempts": 3,
"language": null
}
}
Guarde o id junto do cadastro: ele volta no webhook como linkId. Crie um link por pessoa. Quem tiver a url pode fazer a verificação no lugar dela, então envie o link só para o destinatário e use a validade e o número de tentativas mais curtos que o seu fluxo permitir.
Passo 2: entender a decisão
Ao terminar, a pessoa volta para o redirectUrl com kyc_link_id e external_id na URL, mas sem a decisão. A decisão vem de uma destas duas fontes, e só dessas.
Consulta pela API
GET /kyc/links/{id} devolve o estado do link e, quando a pessoa já terminou, o objeto verification. O status do link pode ser pending, completed, expired ou canceled. A url só volta enquanto o link pode ser usado, e verification é null até a pessoa terminar. A resposta não traz dados pessoais.
Os três resultados
verification.status | Significado | O que fazer |
|---|---|---|
approved | Tudo certo. O link é encerrado. | Libere o cadastro. |
manual_review | Precisa de uma pessoa da sua equipe. O link é encerrado. | Revise no painel (veja a seção sobre revisão manual). |
rejected | Reprovada pelas regras da conta. | A pessoa pode tentar de novo enquanto houver tentativas. |
Os motivos chegam em reasons, no formato [{ "code", "message" }]. Trate um code desconhecido como caso de revisão e mostre o message apenas para a sua equipe, nunca para o usuário final.
Passo 3: receber a decisão por webhook
O webhook evita que você precise consultar a API em loop. Cada decisão, automática ou tomada pela sua equipe no painel, gera um POST para a URL cadastrada. O evento não traz dados pessoais: só IDs, a decisão e os motivos.
Configuração
No painel, em Verificações > Configurações > Integrações:
- Cadastre a URL do seu endpoint. Só
httpse só endereços públicos. - Copie o segredo e guarde em uma variável de ambiente, como
CPFHUB_WEBHOOK_SECRET. - Use o envio de teste para receber um evento
kyc.teste conferir a assinatura.
Formato do evento
{
"event": "kyc.decision",
"eventId": "3e8a1f20-5c4b-4d9e-8f7a-6b2c1d0e9f8a",
"verificationId": "8f3c2a1e-77b0-4d3c-9a1f-0e5b6c7d8e9f",
"linkId": "5b1d7c2e-8a4f-4e7b-9c3d-2f6a1e0b9d47",
"externalId": "cliente-4821",
"status": "approved",
"previousStatus": null,
"decidedBy": "automatic",
"reasons": [],
"occurredAt": "2026-11-05T14:06:12.000Z"
}
A requisição também traz os headers x-cpfhub-event, x-cpfhub-delivery (use para ignorar repetições) e x-cpfhub-signature, no formato t=<unix>,v1=<hex>.
Verificando a assinatura
v1 é o HMAC-SHA256, em hexadecimal, de "<t>.<corpo cru>" calculado com o seu segredo. Três cuidados evitam a maior parte dos erros: calcular sobre o corpo exatamente como chegou, antes de qualquer parse de JSON; comparar em tempo constante; e recusar eventos com t a mais de 5 minutos do seu relógio.
import crypto from 'node:crypto'
import express from 'express'
const app = express()
// Corpo cru: a assinatura é calculada sobre os bytes recebidos
app.post('/webhooks/cpfhub', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.get('x-cpfhub-signature') ?? ''
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
const t = Number(parts.t)
const expected = crypto
.createHmac('sha256', process.env.CPFHUB_WEBHOOK_SECRET!)
.update(`${parts.t}.${req.body}`)
.digest()
const received = Buffer.from(parts.v1 ?? '', 'hex')
const valid =
received.length === expected.length &&
crypto.timingSafeEqual(received, expected) &&
Math.abs(Date.now() / 1000 - t) < 300
if (!valid) return res.sendStatus(401)
const event = JSON.parse(req.body.toString())
if (event.event === 'kyc.decision') {
// Ligue a decisão ao seu cadastro pelo externalId
console.log(event.externalId, event.status)
}
res.sendStatus(200)
})
Entrega e retentativas
-
Confirmação: responda com qualquer
2xxem até 10 segundos e processe o evento depois, em uma fila. -
Retentativas: sem
2xx, a CPFHub.io tenta de novo em 1 min, 5 min, 30 min, 2 h e 12 h. Depois disso a entrega fica como falha e pode ser reenviada pelo painel. -
Repetições: o mesmo evento pode chegar mais de uma vez. Ignore um
x-cpfhub-deliveryque você já processou. -
Ordem: não há garantia de ordem entre eventos. Use
occurredAte, na dúvida, confirme a decisão atual comGET /kyc/links/{id}.
Nunca libere o cadastro com base apenas no redirecionamento. A pessoa pode abrir a URL de retorno sozinha, então só um webhook com assinatura válida, ou a consulta pela API, prova a decisão. Mais detalhes em Webhook da Verificação de Identidade.
Passo 4: tratar a revisão manual
Nem toda verificação é um sim ou um não. Quando o rosto fica em uma zona de dúvida ou o CPF informado diverge do documento, a decisão é manual_review, e uma pessoa da sua equipe precisa olhar o caso no painel.
Um fluxo simples para a equipe:
- Ao receber
manual_review, marque o cadastro como "em análise" usando oexternalIde não libere o acesso. - Abra o caso no painel, em Verificações, e leia os
reasonspara saber o que conferir. - Aprove ou reprove no painel. Essa decisão gera um novo webhook com
decidedBy: "manual"epreviousStatus: "manual_review". - No seu backend, trate esse segundo evento como a decisão final do cadastro.
Os limites de semelhança facial, os documentos aceitos, o tratamento de documento vencido e a retenção ficam em Verificações > Configurações e valem para todos os links da conta. Ajustar esses limites muda a proporção entre aprovações automáticas e casos enviados para a equipe.
Tratando erros
| Status | Quando acontece | O que fazer |
|---|---|---|
400 | Parâmetro inválido (redirectUrl sem https, valor fora do permitido) | Corrija o body. Não repita. |
401 | API Key ausente, inválida ou inativa | Confira o header x-api-key. |
403 | Conta inativa ou bloqueada | Fale com o suporte. |
404 | Link não existe ou é de outra API Key | Confira o id. |
503 | Falha passageira | Repita com espera crescente (2 s, 4 s, 8 s). |
O formato dos erros segue o padrão descrito em Códigos de Erro. Como error pode vir como string ou como objeto com message, o exemplo em TypeScript acima já trata os dois formatos.
Perguntas frequentes
Quanto custa cada verificação?
São 5 créditos por verificação concluída, do mesmo saldo das consultas de CPF, inclusive quando a pessoa é reprovada. Criar e consultar links não consome créditos, e falha técnica da CPFHub.io não cobra.
Posso usar no plano Grátis?
Sim. A verificação está liberada em todos os planos, inclusive no Grátis, que tem 50 créditos por mês. Cada verificação concluída consome 5 desses créditos.
Posso saber o resultado só pelo redirecionamento?
Não. O redirecionamento traz apenas kyc_link_id e external_id, sem a decisão, e a pessoa pode abrir a URL de retorno sozinha. Confirme sempre pelo webhook assinado ou por GET /kyc/links/{id}.
O que acontece se meu endpoint ficar fora do ar?
A CPFHub.io repete a entrega em 1 min, 5 min, 30 min, 2 h e 12 h. Depois disso a entrega fica como falha e pode ser reenviada pelo painel. Como não há garantia de ordem, consulte a API quando precisar da decisão atual.
A verificação aceita CNH Digital?
Não. Os documentos aceitos são CNH, RG e passaporte, e os tipos permitidos para a sua conta são configurados no painel.
Conclusão
Verificar identidade com documento e selfie via API se resume a quatro ações: criar um link por pessoa, guardar o id e o externalId, validar a assinatura do webhook e dar à sua equipe um caminho claro para os casos em manual_review. A CPFHub.io cuida da captura, da prova de vida, da leitura do documento e da comparação facial.
Para começar, crie sua conta em app.cpfhub.io, gere sua API Key e consulte a documentação da Verificação de Identidade.
CPFHub.io
Pronto para integrar a API?
50 créditos gratuitos para testar agora. Sem cartão de crédito. Acesso imediato à documentação.




