> ## Documentation Index
> Fetch the complete documentation index at: https://docs.unifokal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Consulta cadastral de CPF e de CNPJ

> Os sete módulos de consulta cadastral de CPF e de CNPJ, com o que cada um traz e o payload de resultado de cada consulta.

## Consulta cadastral de CPF e de CNPJ

Sete módulos consultam a **fonte cadastral** a partir do documento já validado: `cpf_status` (situação na Receita e óbito), `cpf_contacts` (telefones e e-mails), `cpf_addresses`, `cpf_companies` (empresas no nome do titular), `cnpj_registration`, `cnpj_realtime` (cadastro em tempo real, com CNAE, porte e quadro societário) e `cnpj_shareholdings` (o QSA detalhado, com percentual de cada sócio). Todos exigem **Documento, Face Match e Liveness** no mesmo flow, e a razão é a de sempre: o CPF ou CNPJ consultado vem do **documento apresentado** por quem está vivo na frente da câmera, nunca de um número digitado. Sem esse portão, a plataforma viraria uma ferramenta de consulta cadastral de terceiros.

> [!WARNING]
>
> **Aqui o `data` é o dossiê da fonte, e não um objeto desenhado por nós.** Tiramos exatamente **três** campos, que são de conta e não do titular (`pacoteUsado`, `saldo` e `consultaID`), e o resto passa como a fonte mandou. Isso é decisão de produto, não descuido: remapear campo de fonte externa cria um contrato nosso que envelhece calado quando a fonte muda. A consequência para você é direta: **o conjunto de chaves varia por pacote e por consulta**, e pode ganhar campo sem release nosso. Os exemplos abaixo são **a forma observada**, não uma lista fechada. Leia por chave, com valor ausente tratado como ausente, e nunca escreva um parser estrito sobre estes quatro blocos.

**Duas armadilhas de leitura, e as duas já custaram integração alheia.** A primeira: `status` **não é a situação cadastral do titular**, é o indicador de sucesso da chamada à fonte (`1` quer dizer "a consulta deu certo"). A situação cadastral é `situacao`, e ela vem como **texto** no CPF (`"Regular"`, `"Titular Falecido"`) e como **objeto** no CNPJ (`{ "id": 2, "nome": "Ativa", … }`). A segunda: as datas do dossiê vêm no formato brasileiro `DD/MM/AAAA`, e não em ISO, com uma exceção que é justamente onde você não espera (veja `data_entrada` logo abaixo).

**A chave `data` se comporta de dois jeitos diferentes**, e vale saber qual é qual. Nos quatro tiers de **CPF** a chave **sempre existe** e vem `null` quando não houve enriquecimento (fonte fora, documento não encontrado, ou o portão de identidade não liberou). Nos três tiers de **CNPJ** a chave é **omitida** quando não há nem dossiê nem screening de sócios. Um parser que assume "`data` sempre presente" erra no CNPJ; um que assume "`data` ausente significa nada consta" erra nos dois.

**Quando a própria consulta cadastral recusa a verificação, nenhum dado cadastral sai.** Se o `status` é `denied` e o motivo vem de um módulo cadastral (os quatro tiers de CPF, os de CNPJ, a Coerência Cadastral ou a entrega de dados cadastrais), como no óbito registrado na fonte, todo módulo cadastral da verificação chega **sem o bloco `data`**, e a Coerência Cadastral chega sem o resultado campo a campo, sem dizer qual dado divergiu. Você recebe o desfecho e o `reason_code`, que bastam para decidir, no webhook, na API e no painel. É a regra de necessidade da LGPD (art. 6º, III): quem foi recusado não vira consulta cadastral de terceiro. Nos demais desfechos nada muda, e o dado cadastral continua saindo só depois que a identidade foi comprovada.

```
// cpf_status: situação na Receita e o marcador de óbito
{ "module": "cpf_status", "passed": true, "outcome": "approved", "score": 95,
  "data": { "status": 1,                   // sucesso da CONSULTA, não situação do titular
            "cpf": "12345678901", "nome": "TITULAR MOCK DA SILVA",
            "nascimento": "15/03/1990",    // DD/MM/AAAA
            "situacao": "Regular",         // <- ESTA é a situação cadastral
            "situacaoInscricao": "anterior a 10/11/1990", "situacaoDigito": "00",
            "situacaoMotivo": null, "situacaoAnoObito": null,
            "situacaoComprovante": "1A1A.2B2B.3C3C.4D4D",
            "situacaoComprovanteEmissao": "29/06/2026 19:08:44" } }

// cpf_addresses: o endereço principal vem SOLTO no topo, e o histórico em "enderecos"
{ "module": "cpf_addresses", "passed": true, "outcome": "approved", "score": 95,
  "data": { "status": 1, "cpf": "12345678901", "nome": "TITULAR MOCK DA SILVA",
            "nascimento": "15/03/1990", "situacao": "Regular",
            "endereco": "Rua Mock", "numero": "100 B", "complemento": "Apto 03",
            "bairro": "Centro", "cep": "99999123", "cidade": "Sao Paulo",
            "uf": "SP", "ibge": "1234567",
            "enderecos": [ { "endereco": "Rua Mock Antiga", "numero": "200",
                             "bairro": "Centro", "cep": "99999123",
                             "cidade": "Sao Paulo", "uf": "SP", "ibge": "1234567" } ] } }

// cpf_contacts: telefones, WhatsApp e e-mails, cada um como LISTA (pode vir vazia)
{ "module": "cpf_contacts", "passed": true, "outcome": "approved", "score": 95,
  "data": { "status": 1, "cpf": "12345678901", "nome": "TITULAR MOCK DA SILVA",
            "telefones": [ "11999999999", "3188888888" ],
            "whatsapp": [ "11999999999" ],   // subconjunto de "telefones"
            "emails": [ "titular@exemplo.com" ] } }
// este tier NÃO traz "situacao": ele fala de contato, não da situação cadastral do CPF.

// cpf_companies: as empresas em que o titular consta como sócio
{ "module": "cpf_companies", "passed": true, "outcome": "approved", "score": 95,
  "data": { "status": 1, "cpf": "12345678901", "nome": "TITULAR MOCK DA SILVA",
            "empresas": [ { "cnpj": "77888999000110", "razao": "LOJA MOCK LTDA",
                            "fantasia": "MINHA LOJA", "dataSociedade": "01/02/2018",
                            "qualificacao": "SOCIO-ADMINISTRADOR", "situacao": "Ativa" } ] } }
// este tier NÃO traz "situacao" do titular no topo: ele fala das empresas, não do CPF.

// fonte fora do ar -> pending, e a chave "data" EXISTE, vinda null
{ "module": "cpf_status", "passed": null, "outcome": "pending", "score": 0,
  "reason": "gov_unavailable", "data": null }

// limite de uso da conta atingido -> pending, sem cobrança; o limite volta às 00:00 UTC
{ "module": "cpf_status", "passed": null, "outcome": "pending", "score": 0,
  "reason": "account_usage_limit", "data": null }

// documento inexistente na base oficial -> este caminho REPROVA, e o data também vem null
{ "module": "cpf_status", "passed": false, "outcome": "failed", "score": 10, "data": null }
```

**Manutenção programada da fonte oficial.** Quando um órgão anuncia uma parada programada de um serviço que estes módulos consultam, a [página de status](https://unifokal.com/status) mostra a janela prevista antes de ela começar, com o horário, e tira a janela do ar sozinha quando ela termina. Durante a janela, as consultas que dependem daquela fonte seguem o caminho de fonte fora do ar mostrado acima.

**Consulta cadastral fora, aprovação segura.** Quando a consulta cadastral de um flow não responde na análise e uma checagem da decisão depende do dado dela, a verificação que seria aprovada vai para revisão humana com `decision_reason: "registration_source_unavailable"`. Recusa e bloqueio não mudam. Para o titular o código é `manual_review`, e reprocessar a verificação no painel refaz só a consulta que faltou.

**A consulta é refeita sozinha.** Quando a fonte não responde, a consulta cadastral é refeita sozinha por tempo limitado depois da decisão, sem nova ação sua nem do titular. Se a fonte voltar nesse tempo, chega um webhook novo da mesma verificação, com `revision`, trazendo o dado no check e sem cobrança a mais. Se não voltar, o check segue `pending` com `gov_unavailable`, e o reprocesso pelo painel continua disponível.

Nos **tiers de CNPJ** o dossiê vem aninhado em `company`, e ao lado dele pode viajar `partner_screening`, que é o [screening do quadro societário](https://docs.unifokal.com/modulos/screening-socios.md#screening-socios) descrito na próxima seção. Os três tiers entregam **conjuntos bem diferentes**: `cnpj_registration` é o cadastro básico pela base pública mensal da Receita, com o mês de referência da base em `reference_month` (a última base mensal carregada por inteiro: enquanto a atualização do mês seguinte roda, ou se ela parar no meio, um cadastro pode já vir da base seguinte, nunca de uma anterior) e a opção pelo Simples Nacional e pelo MEI (sem CNAE, sem porte e **sem sócios**; empresa aberta depois do retrato do mês sai pendente, nunca inexistente), `cnpj_realtime` é o mais completo (acrescenta natureza jurídica, CNAE, porte, Simples Nacional, regimes tributários e o QSA) e `cnpj_shareholdings` é o mais **magro** de todos, porque o produto dele é uma coisa só: o `percentual` de cada sócio.

> [!WARNING]
>
> **O mesmo campo do sócio muda de tipo entre dois tiers.** `qualificacao_socio` é um **objeto** `{ "id": 49, "descricao": "Sócio-Administrador" }` em `cnpj_realtime` e `cnpj_partners`, e uma **string** crua em `cnpj_shareholdings`. E `data_entrada` vem em **ISO** (`"2015-06-01"`) no primeiro caso e no formato **brasileiro** (`"14/11/2018"`) no segundo. Um parser único sobre "o sócio" quebra quando você ligar o segundo tier. Isso é a fonte falando, não nós: os dois pacotes são produtos diferentes dela.

```
// cnpj_realtime: o tier mais completo (recorte legível do dossiê)
{ "module": "cnpj_realtime", "passed": true, "outcome": "approved", "score": 95,
  "data": { "company": {
      "status": 1, "cnpj": "11222333000181", "tipo": "Matriz",
      "razao": "EMPRESA MOCK LTDA", "fantasia": "MOCK",
      "capitalSocial": 100000, "inicioAtividade": "01/06/2015",
      "situacao": { "id": 2, "nome": "Ativa",        // OBJETO no CNPJ (string no CPF)
                    "data": "01/06/2015", "motivo": null },
      "naturezaJuridica": { "codigo": "2062", "descricao": "Sociedade Empresaria Limitada" },
      "cnae": { "fiscal": "6201501", "subClasse": "6201-5/01",
                "descricao": "Desenvolvimento de programas de computador sob encomenda" },
      "porte": { "id": "03", "descricao": "Empresa de Pequeno Porte" },
      "simplesNacional": { "optante": "Não", "inicio": "17/01/2020", "fim": "01/01/2023" },
      "socios": [ { "cpf_cnpj_socio": "111.222.333-44", "nome": "SOCIO MOCK UM",
                    "tipo": "Pessoa Física",
                    "data_entrada": "2015-06-01",              // ISO neste tier
                    "qualificacao_socio": { "id": 49,          // OBJETO neste tier
                                            "descricao": "Sócio-Administrador" } } ] } } }

// cnpj_shareholdings: o QSA detalhado. O produto dele é o "percentual".
{ "module": "cnpj_shareholdings", "passed": true, "outcome": "approved", "score": 95,
  "data": { "company": {
      "status": 1, "cnpj": "11222333000181", "razao": "EMPRESA MOCK LTDA", "fantasia": "MOCK",
      "socios": [ { "cpf_cnpj_socio": "111.222.333-44", "nome": "SOCIO MOCK UM",
                    "qualificacao_socio": "ADMINISTRADOR",   // STRING neste tier
                    "data_entrada": "14/11/2018",            // DD/MM/AAAA neste tier
                    "percentual": 60 },
                  { "cpf_cnpj_socio": "22.333.444/0001-81",
                    "nome": "HOLDING MOCK PARTICIPACOES LTDA",
                    "qualificacao_socio": "SOCIO PESSOA JURIDICA",
                    "data_entrada": "17/09/2015", "percentual": 40 } ] } } }
// este tier NÃO traz situação, CNAE nem endereço: não o use para inferir situação cadastral.

// cnpj_registration: o cadastro básico, pela base pública mensal da Receita. Sem CNAE,
// sem porte e SEM sócios, e por isso ele nunca recebe o bloco "partner_screening".
// "reference_month" é o mês da base consultada; empresa aberta depois dela sai pendente.
{ "module": "cnpj_registration", "passed": true, "outcome": "approved", "score": 95,
  "data": { "company": {
      "reference_month": "2026-09",
      "status": 1, "cnpj": "11222333000181", "tipo": "Matriz",
      "razao": "EMPRESA MOCK LTDA", "fantasia": "MOCK",
      "capitalSocial": 100000, "inicioAtividade": "01/06/2015",
      "email": "contato@mock.com.br",
      "matrizEndereco": { "cep": "39400-000", "tipo": "Rua", "logradouro": "Rua Mock",
                          "numero": "100", "complemento": null, "bairro": "Centro",
                          "cidade": "Montes Claros", "uf": "MG" },
      "telefones": [ { "ddd": "11", "numero": "22334454" } ],
      "situacao": { "id": 2, "nome": "Ativa", "data": "01/06/2015", "motivo": null },
      "simplesNacional": { "optante": "Sim", "inicio": "17/01/2020", "fim": null },
      "simei": { "optante": "Não", "inicio": null, "fim": null } } } }
```

**Quadro societário grande é o caso em que o corpo do webhook estoura o teto.** Quando o corpo passa de **256 KB**, a primeira coisa que podamos é justamente `company.socios`, e o corte é **declarado**: o par `truncated` mais `truncated_fields` no topo diz exatamente o que saiu, como explicado em [Webhooks](https://docs.unifokal.com/webhooks.md#webhooks). A decisão nunca é podada, e o detalhe completo continua no painel.

Em sandbox nada é consultado e o dossiê vem pronto, escolhido pelo **sufixo do documento**: `03` devolve a situação suspensa ou inapta e `99` o documento inexistente. Só no sandbox o dossiê carrega a chave `"mock": true`, que **produção nunca emite**: se você ramificar por ela, o código quebra na virada de ambiente.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.