# Comprovante de endereço

<https://unifokal.com/docs/modulos/endereco-ocr>

## Comprovante de endereço

O módulo `endereco_ocr` lê o **comprovante de endereço** que o titular envia e devolve o endereço impresso nele. Valem conta de luz, água, gás, telefone ou internet, fatura ou extrato bancário, contrato de aluguel e documento de órgão público. Além do endereço, ele responde duas perguntas que a leitura sozinha não responde: **o comprovante é recente?** e **o nome dele é o mesmo do documento de identidade lido neste fluxo?**

**Um passo novo no widget, e zero código a mais do seu lado.** O titular envia um **PDF ou uma foto**, de até **4 MB**, na mesma jornada em que fotografa o documento e faz a selfie. Do PDF vale a **primeira página**: é nela que o comprovante precisa trazer o nome, o endereço e a data de emissão, e o widget avisa isso na tela de envio. O arquivo não passa pelo seu front nem chega ao seu backend, e a mídia fica no painel com acesso restrito por papel, como as demais.

**Dependências e preço.** Ele exige `cpf_ocr` e `face` no mesmo flow, porque o nome cruzado é o que foi lido do documento de identidade: sem eles não há contra o que cruzar. Por isso o número que importa é o do **conjunto**, e não o do módulo sozinho: o flow mínimo com este módulo (`cpf_ocr` + `face` + `endereco_ocr`) custa **-** por verificação, e é esse o valor que entra na sua fatura. O unitário de cada módulo está na [tabela de preços](https://unifokal.com/precos), lida do mesmo catálogo.

```
// endereco_ocr no check_details: o endereço lido, a recência e os dois cruzamentos
{ "module": "endereco_ocr", "passed": true, "outcome": "approved", "score": 92,
  "data": { "doc_type": "utility_bill",   // utility_bill | bank_statement | telecom |
                                          // rent_contract | government | other
            "cep": "01310930", "city": "São Paulo", "uf": "SP",
            "issue_date": "2026-08-20",   // data de emissão lida do comprovante
            "issue_age_days": 27,         // há quantos dias ele foi emitido
            "name_match": true,           // o nome do comprovante é o do documento de identidade?
            "address_match": "match",     // match | mismatch | unknown
            "declared_address_match": "cep_match",  // cep_match | uf_match | mismatch | unknown
            "cep_consistency": "ok" } }  // ok | cep_fora_da_base | uf_divergente | unknown

// nome divergente: NUNCA é recusa. O portão é soft, e a verificação vai para revisão humana.
{ "module": "endereco_ocr", "passed": false, "outcome": "failed", "score": 40,
  "data": { "doc_type": "utility_bill",
            "cep": "01310930", "city": "São Paulo", "uf": "SP",
            "issue_date": "2026-08-20", "issue_age_days": 27,
            "name_match": false, "address_match": "match",
            "reason": "name_mismatch" } }

// comprovante fora do prazo: o titular reenvia um mais novo, ninguém é reprovado por isso.
// passed null + outcome "pending" são o shape de "ainda não dá para afirmar nada".
{ "module": "endereco_ocr", "passed": null, "outcome": "pending", "score": 0,
  "data": { "doc_type": null,
            "cep": "01310930", "city": "São Paulo", "uf": "SP",
            "issue_date": "2025-11-02", "issue_age_days": 318,
            "name_match": null, "address_match": "unknown",
            "reason": "address_doc_expired" } }
// reason só aparece quando existe: no caminho aprovado a chave não vem.
```

No resumo `checks` o módulo aparece com **vocabulário próprio**, e não com o `pass`/`fail` dos módulos de identidade: `verified` (extraiu, está no prazo e o nome cruzou), `mismatch` (o cruzamento de nome reprovou) e `pending` (toda a família de indeterminado, com o motivo fino em `data.reason`). Aqui não há lista consultada, há um documento conferido, e o vocabulário diz isso.

**A recência é regra do produto, não detalhe.** Um comprovante vale por até **90 dias** contados da emissão. A idade em dias vem no payload (`issue_age_days`) para você aplicar uma régua mais apertada se a sua política pedir: a nossa é o teto, nunca o piso.

**Os motivos, e o que fazer com cada um.** Eles vêm em `reason` dentro do `data` do check, e se dividem em dois grupos com ações opostas. **Pedem o reenvio do arquivo**, e nunca são veredito contra o titular: `address_not_found` (o endereço não foi localizado no comprovante), `address_doc_expired` (o comprovante passou dos 90 dias) e `address_doc_unreadable` (o arquivo não pôde ser lido). **Levam a revisão humana**, com a evidência na mão de quem revisa: `name_mismatch` (o nome do comprovante diverge do nome do documento), `identity_name_unavailable` (o nome do documento não estava disponível para o cruzamento) e `injection_suspected_text` (o arquivo pede conferência humana antes de qualquer cruzamento) e `address_doc_reused` (o mesmo arquivo de comprovante já foi enviado por outro titular da sua conta; nunca compara com outras contas). Quando o módulo segura a verificação, o `decision_reason` dela é `address_proof_review`.

! **Verificação documental com cruzamento de nome, e não prova absoluta de residência.** O que o módulo afirma é o que o documento diz e se esse documento é do titular do fluxo. Conta de luz em nome do cônjuge, do pai ou do locador é comum e legítima no Brasil, e por isso **nome divergente nunca é recusa automática**: vai para revisão humana, e quem decide aceitar aquele comprovante é você.

**Opcional: o endereço que você já tem em cadastro.** Na criação da sessão, do seu servidor, você pode enviar `expected_address` e ligar o sinal `address_match`. Ele aceita **só CEP e UF**, de propósito: é o recorte que responde "é o mesmo endereço?" sem que você precise nos mandar a rua e o número do titular. **Sem o campo, o sinal sai `unknown`**, que não é `mismatch` e muito menos `match`: é "não havia com o que comparar".

```
// criação de sessão (servidor, sk_): o campo é opcional e só aceita CEP e UF
{ "flow_id": "flow_...", "reference_id": "user_123",
  "expected_address": { "cep": "01310930", "uf": "SP" } }
```

Mandar `expected_address` num flow que não tem `endereco_ocr` é `422 expected_address_not_supported`, nunca aceito e ignorado. CEP ou UF fora do formato é `400 validation_error` com o campo nomeado.

Em sandbox nada é lido: o desfecho vem do sufixo do documento. `01` devolve o reenvio por endereço não localizado, `02` o nome divergente (que vai para revisão) e qualquer outro sufixo aprova. Com `expected_address` na sessão, o `address_match` do sandbox também vem do sufixo: `83` devolve `mismatch` e os demais `match`. Sem o campo, `unknown`, como em produção.

Quando o fluxo também tem a consulta cadastral de endereços, o `data` traz `declared_address_match`: o CEP e a UF do comprovante contra os endereços dessa consulta, com `cep_match`, `uf_match`, `mismatch` ou `unknown` quando falta um dos lados. É informação para você e nunca reprova a verificação.

Em todo comprovante o `data` traz também `cep_consistency`: o CEP lido conferido contra uma base de endereços, com `ok` (o CEP está na base, na mesma UF do comprovante), `uf_divergente` (está na base, em outra UF), `cep_fora_da_base` ou `unknown` (não houve conferência, por exemplo quando o CEP não foi lido). Fora da base não quer dizer inexistente: CEP novo ou de grande usuário pode faltar nela. É informação para a sua política, e nunca muda o desfecho da verificação.
