Skip to content

Fluxo de OCR + Biometria

Um onboarding por documento responde a quatro perguntas, sempre nesta ordem:

  1. Que documento é este?
  2. O que está escrito nele — e bate com a Receita Federal?
  3. A pessoa da selfie é a mesma do documento?
  4. Esse CPF corresponde a uma pessoa registrada?

Cada pergunta tem um endpoint, e a referência descreve todos eles em detalhe. O que ela não descreve é a ordem, o que fazer quando um passo não devolve o esperado e como o resultado de um passo alimenta o seguinte — que é o assunto desta página.

A primeira pergunta é a única opcional: o Full OCR já classifica o documento por dentro. Quando vale chamar o classificador antes é a primeira decisão do fluxo.

Visão geral

PassoEndpointPergunta que ele respondeO que você leva adiante
1
(opcional)
POST /classify/v3Que documento é este, e qual lado eu recebi?a decisão de seguir, pedir o outro lado, ou rotear
2POST /full-ocr/v4O que está escrito, e confere com a Receita?enhanced.person.taxId e a imagem do documento
3POST /face-match/v2A selfie é da mesma pessoa do documento?matched e confidence
4GET /bureau/v2/natural-person/{CPF}Esse CPF corresponde a uma pessoa registrada?nome, nome da mãe e data de nascimento oficiais
  arquivos do documento
  (frente + verso)

          │  opcional — só quando há uma decisão a tomar antes de extrair
          ├──▶  POST /classify/v3
          │       → data[0].classification.type / .subtype / .sides[].side


    POST /full-ocr/v4?returnsFaceInfo=true
      → data[0].enhanced.person ........ os dados lidos, já normalizados
      → data[0].taxData e .matches ..... cruzamento com a Receita Federal
      → data[0].classification ......... que documento era, afinal

          │  + a selfie do usuário

    POST /face-match/v2
      → data[0].matched e data[0].confidence

          │  com o enhanced.person.taxId

    GET /bureau/v2/natural-person/{taxId}
      → data.name, data.mothersName, data.birthdate

‼️ Informe a versão na URL do Full OCR

POST /full-ocr, sem versão no caminho, é atendido pela v2 — não pela v4. A v2 tem outro formato de resposta, não separa extraction de enhanced e ignora todos os parâmetros de query. Chame sempre /full-ocr/v4.

Passo 1 — classificar, e quando pular

O Classificador responde que documento é este e nada mais: tipo, subtipo, país e lado, com a confiança da classificação. Ele não lê o conteúdo do documento.

O Full OCR faz a mesma classificação por dentro — ela volta em data[].classification de toda resposta — e extrai os campos e cruza com a Receita Federal, tudo em uma chamada. Por isso, a maioria dos fluxos não precisa do passo 1.

A pergunta que decide é simples: existe alguma decisão a tomar antes de extrair?

Chame o /classify/v3 antes quando…Vá direto ao /full-ocr/v4 quando…
Você precisa validar a captura enquanto o usuário ainda está na tela — descobrir que só chegou o verso e pedir a frente antes de gastar a extração.Você já sabe que vai extrair de qualquer forma. A classificação vem junto, sem custo adicional.
Você precisa rotear por tipo: comprovante de residência tem endpoint dedicado, e outros tipos podem simplesmente não servir ao fluxo.O seu fluxo aceita um conjunto fechado de documentos e trata tudo o que não for reconhecido do mesmo jeito.
Você quer recusar cedo um documento fora da lista aceita, sem pagar pela análise completa.Você recebe frente e verso juntos e confia na captura.

🚧 O passo 1 é uma chamada a mais, e ela é tarifada

Toda resposta 2xx gera cobrança, inclusive uma classificação que não encontrou documento nenhum — veja Tarifação. Classificar "por via das dúvidas", antes de um /full-ocr que aconteceria de qualquer jeito, dobra o número de chamadas para obter a mesma informação.

Some-se a isso que o /classify/v3 aceita um arquivo por chamada, enquanto o /full-ocr aceita até sete: classificar frente e verso são duas chamadas, e depois ainda vem a do OCR.

A saída do classificador é uma lista ordenada do melhor para o pior resultado. Uma imagem sem documento reconhecível devolve 200 com "data": [] — não é erro, é resposta. Leia data[0] só depois de confirmar que a lista não está vazia.

Passo 2 — extrair os dados e cruzar com a Receita

O Full OCR devolve, para cada documento reconhecido, três blocos que interessam ao fluxo:

  • extraction — o texto como saiu do OCR, cru.
  • enhanced — o mesmo dado normalizado e corrigido. É este que você persiste e é dele que sai o CPF do passo 4, em enhanced.person.taxId, sem máscara.
  • taxData e matches — o que a Receita Federal devolveu e o resultado campo a campo da comparação com o que estava escrito no documento.
bash
curl -i -X POST 'https://api-homolog.nxcd.app/full-ocr/v4?returnsFaceInfo=true' \
  --header 'Authorization: ApiKey SUA_CHAVE_AQUI' \
  --form 'frente=@./cnh-frente.jpg' \
  --form 'verso=@./cnh-verso.jpg'

Envie o documento inteiro — o cruzamento depende do lado

O CPF não está impresso nos dois lados de todo documento, então a consulta à Receita Federal depende da face enviada. Uma CNH só com o verso, por exemplo, não dispara a consulta: taxData e matches continuam na resposta, mas vazios e com todos os matches em false.

Esse é o principal motivo prático para validar a captura antes — seja no passo 1, seja na sua própria interface. As regras por tipo de documento estão em taxData e matches.

Duas leituras que costumam ser feitas errado:

  • data é uma lista, ordenada do melhor para o pior resultado. Se você espera um único documento, leia data[0] — mas confirme antes que a lista não veio vazia. Arquivo ilegível devolve 200 com "data": [].
  • matches.name: false nem sempre significa "nome diferente". Um nome lido com menos de 8 caracteres — sintoma típico de OCR que falhou — é tratado como não conferido, e o cruzamento para ali. Nesse caso, o caminho é melhorar a captura, não reprovar a pessoa.

Precisa também da biometria oficial?

O Full OCR + Datavalid soma a validação no Datavalid da SERPRO à mesma chamada. Ele substitui este passo; não se acumula com ele.

Passo 3 — comparar a face do documento com a selfie

Até aqui, o fluxo provou que o documento existe e que os dados dele conferem com a Receita. Falta provar que quem está enviando é a pessoa do documento.

O Face Match recebe duas imagens na mesma requisição — a do documento e a selfie — e responde se as faces são da mesma pessoa:

bash
curl -i -X POST 'https://api-homolog.nxcd.app/face-match/v2' \
  --header 'Authorization: ApiKey SUA_CHAVE_AQUI' \
  --form 'documento=@./cnh-frente.jpg' \
  --form 'selfie=@./selfie.jpg'

Os nomes dos campos são livres e voltam na resposta, dentro de data[].resources[].fieldname — é por eles que você identifica qual imagem é qual.

🚧 "data": [] não é erro, e data[0] pode não existir

A comparação só acontece quando as duas imagens têm alguma face detectável. Faltando uma face, a resposta é 200 com a lista vazia, e um código que lê data[0].matched direto quebra em vez de receber um resultado negativo. Confira o tamanho de data antes.

Três decisões deste passo:

  • A selfie veio de uma sessão de liveness? Então o endpoint é outro: o Face Match for Liveness reaproveita a selfie já capturada e você envia só o documento. O encadeamento completo está no Fluxo de Liveness — e é ele que acrescenta o liveness, que o /face-match sozinho não faz: comparar faces não distingue uma pessoa presente de uma foto de foto.
  • Qual imagem do documento enviar? A original serve. Se você quiser exibir ou auditar o recorte da face que estava no documento, o /full-ocr/v4 com returnsFaceInfo=true já devolveu esse recorte em data[].face.croppedBase64 — mas a comparação em si é sempre uma chamada ao /face-match.
  • Precisa da biometria do governo? O Face Match + Datavalid confronta a face com a base oficial a partir de um CPF.

Um parâmetro de query errado se comporta de forma diferente em cada endpoint

No /face-match, um parâmetro desconhecido derruba a requisição com 422. No /full-ocr, o mesmo erro é ignorado em silêncio: a resposta vem 200, sem o campo que você esperava. Um returnFaceInfo escrito sem o s não avisa nada — só devolve uma resposta sem a face.

Passo 4 — cruzar com o bureau

O Bureau PF consulta os dados da Receita Federal a partir do CPF:

GET /bureau/v2/natural-person/12345678909

O CPF sai de data[0].enhanced.person.taxId do passo 2, já sem máscara — embora o endpoint aceite as duas formas, com ou sem.

Nem todo fluxo precisa deste passo. Quando o /full-ocr/v4 conseguiu cruzar, taxData já traz nome, nome da mãe e data de nascimento oficiais, e matches já diz o que bateu. O Bureau PF é o caminho direto quando:

  • o documento não carrega CPF, ou o cruzamento não aconteceu porque faltou o lado certo — os blocos vieram vazios;
  • o CPF que você quer verificar foi digitado pelo usuário, e não lido do documento, e você precisa confrontá-lo com o que o documento disse;
  • você precisa dos dados oficiais em um momento do fluxo em que não há documento nenhum em mãos.

🚧 CPF não encontrado devolve 200 com "data": {}

Não é 404. Teste se o objeto veio vazio antes de ler data.name. E a chamada é tarifada do mesmo jeito, porque houve processamento.

Cuidado ao comparar datas entre os dois endpoints

taxData.birthdate, no Full OCR, é uma string no formato "1990-02-01". data.birthdate, no Bureau PF, é um número no formato 19990201. Normalize antes de comparar — a comparação direta entre os dois nunca dá igual.

Quando um passo não devolve o esperado

SintomaCausa provávelO que fazer
Resposta em formato inesperado, sem enhancedPOST /full-ocr chamado sem versão: a chamada foi atendida pela v2.Usar /full-ocr/v4. Confira o campo version da resposta.
Parâmetro de query sem efeito no Full OCRNome escrito errado, ou parâmetro que só vale na v4 usado em outra versão. Nenhum dos dois gera erro.Conferir a grafia e a versão. Booleanos só ligam com a string exata true.
"data": [] no classificador ou no Full OCRNenhum documento reconhecido — imagem borrada, cortada, escura, ou tipo fora da lista aceita.Não é erro nem falha: é 200 tarifado. Peça nova captura.
taxData e matches vazios, todos os matches em falseO lado enviado não permite a consulta à Receita, ou o documento não tem CPF a cruzar (CRLV, ANTT).Reenviar com frente e verso. As regras por tipo estão na referência.
matches.name: false com o nome visivelmente certoNome lido com menos de 8 caracteres, tratado como não conferido; ou OCR com ruído.Comparar com o extraction para ver o que foi lido de fato, e melhorar a captura.
"data": [] no Face MatchUma das imagens não tem face detectável — documento cortado, selfie escura, PDF cuja primeira página com face não é a esperada.Verificar o tamanho de data antes de ler data[0], e reenviar as imagens.
422 no Face MatchParâmetro de query desconhecido ou com valor inválido, formato de arquivo recusado, ou mais de dois arquivos.O /face-match recebe exatamente duas imagens.
422 no Full OCR logo na entradafederalRevenueNumber que não passa na validação do dígito verificador, ou mais de sete arquivos.O CPF é validado antes de qualquer processamento e derruba a requisição inteira.
"data": {} no Bureau PFCPF não localizado.Tratar como "não encontrado", não como erro. A resposta é 200.

A lista completa de códigos e o formato das respostas de erro estão em Códigos HTTP das respostas. As duas formas de enviar arquivos — multipart e base64 — e os limites de tamanho estão em Envio de arquivos.

O que encadeia com o quê

ValorNasce emÉ usado em
classification.type / .sides[].sidePOST /classify/v3a sua decisão de rotear, pedir o outro lado ou seguir
enhanced.person.taxIdPOST /full-ocr/v4caminho do GET /bureau/v2/natural-person/{CPF}
a imagem do documentoa captura do usuárioPOST /full-ocr/v4 e POST /face-match/v2
Nextid-ReqIdheader de todas as respostasseus logs — é por ele que o suporte localiza uma chamada

Próximos passos

  • Classificador — a lista completa de tipos, subtipos e lados reconhecidos
  • Full OCR — todos os blocos da resposta, os parâmetros de query e as diferenças entre v2, v3 e v4
  • Face Match — a resposta completa, os parâmetros de query e os erros
  • Bureau PF — os campos devolvidos pela consulta
  • Fluxo de Liveness — como somar o liveness a este fluxo

Nextcode | Soluções em Verificação de Identidade