# Consulta cadastral de CPF e de CNPJ

<https://unifokal.com/docs/modulos/cadastrais>

## Consulta cadastral de CPF e de CNPJ

Sete módulos consultam a **fonte cadastral** a partir do documento já validado: `cpf_receita` (situação na Receita e óbito), `cpf_contatos` (telefones e e-mails), `cpf_enderecos`, `cpf_empresas` (empresas no nome do titular), `cnpj_cadastro`, `cnpj_receita` (cadastro em tempo real, com CNAE, porte e quadro societário) e `cnpj_participacoes` (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.

! **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.

```
// cpf_receita: situação na Receita e o marcador de óbito
{ "module": "cpf_receita", "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
            "mae": "MAE MOCK DE SOUZA", "genero": "M",
            "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_enderecos: o endereço principal vem SOLTO no topo, e o histórico em "enderecos"
{ "module": "cpf_enderecos", "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_contatos: telefones, WhatsApp e e-mails, cada um como LISTA (pode vir vazia)
{ "module": "cpf_contatos", "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_empresas: as empresas em que o titular consta como sócio
{ "module": "cpf_empresas", "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_receita", "passed": null, "outcome": "pending", "score": 0,
  "reason": "gov_unavailable", "data": null }

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

**Manutenção programada da fonte oficial.** Quando um órgão para, de forma programada, um serviço que estes módulos consultam, as consultas que dependem daquela fonte seguem o caminho de fonte fora do ar mostrado acima.

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://unifokal.com/docs/modulos/screening-socios#screening-socios) descrito na próxima seção. Os três tiers entregam **conjuntos bem diferentes**: `cnpj_cadastro` é o cadastro básico (sem CNAE, sem porte e **sem sócios**), `cnpj_receita` é o mais completo (acrescenta natureza jurídica, CNAE, porte, Simples Nacional, regimes tributários e o QSA) e `cnpj_participacoes` é o mais **magro** de todos, porque o produto dele é uma coisa só: o `percentual` de cada sócio.

! **O mesmo campo do sócio muda de tipo entre dois tiers.** `qualificacao_socio` é um **objeto** `{ "id": 49, "descricao": "Sócio-Administrador" }` em `cnpj_receita` e `cnpj_socios`, e uma **string** crua em `cnpj_participacoes`. 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_receita: o tier mais completo (recorte legível do dossiê)
{ "module": "cnpj_receita", "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_participacoes: o QSA detalhado. O produto dele é o "percentual".
{ "module": "cnpj_participacoes", "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_cadastro: o cadastro básico. Sem CNAE, sem porte e SEM sócios,
// e por isso ele nunca recebe o bloco "partner_screening".
{ "module": "cnpj_cadastro", "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",
      "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 } } } }
```

**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://unifokal.com/docs/webhooks#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.
