# PEP e listas restritivas

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

## PEP e listas restritivas

O módulo `pep_sancoes` confere o **CPF e o nome lidos do documento** contra listas oficiais de pessoas expostas politicamente e de sanções. Ele exige **Verificação de Identidade + Face Match + Liveness** no mesmo flow: o dado consultado vem do documento apresentado por quem está vivo na frente da câmera, nunca de um CPF digitado. O widget não ganha nenhum passo novo.

! **Este módulo nunca reprova sozinho.** Um resultado positivo é **candidato**, não veredito: nomes brasileiros repetem muito (dentro da própria lista de PEP há um nome com 13 CPFs diferentes). Por isso um sinal leva a verificação para `review`, com a evidência (lista, referência da entrada, score e motivo) no webhook, para a sua análise decidir. Nunca a `denied` automático.

**Cobertura desta fase**, e nada além dela: PEP federal e expulsões da administração federal (CGU: `PEP` e `CEAF`), empresas e pessoas inidôneas ou punidas (CGU: `CEIS` e `CNEP`) e as listas internacionais de sanções da **ONU** (obrigatória no Brasil pela Lei 13.810/2019), do **OFAC** (Tesouro dos EUA) e do **Reino Unido** (UK Sanctions List, do FCDO). **Não** estão incluídos, e não os anunciamos: a lista de sanções da **União Europeia** (a fonte exige uma credencial que ainda não temos, então ela nasce desabilitada e sai marcada como tal em `dataset_versions`), parentes e associados de PEP, PEP estadual/municipal, PEP estrangeiro, improbidade do CNJ e adverse media.

Cada resposta carimba a **idade e o tamanho de cada lista** em `dataset_versions` (`ingested_at`, `age_hours` e `record_count`). Se uma lista que entraria na resposta estiver vencida, o módulo devolve `pending` com `dataset_stale` e a verificação vai para `review`: nós não afirmamos "nada consta" sobre uma base que não conseguimos atualizar, e esse caso **não é cobrado**. Sem CPF legível no documento o resultado também é `pending`.

**A mesma regra vale quando a lista não foi consultada, e não só quando ela envelheceu.** A resposta sai `pending` com `no_coverage` quando nenhuma lista do módulo está disponível, e com `critical_source_disabled` quando uma lista que a resposta não pode dispensar está fora do ar. Os dois casos vão para `review` e **não são cobrados**, pelo mesmo motivo do `dataset_stale`: um veredito de "nada consta" só vale se alguma lista tiver sido de fato consultada. E quando uma lista específica não entrou na consulta, ela aparece em `aggregates.by_list` com `null` em vez de sumir do objeto, para que "não foi olhada" nunca se pareça com "olhada e sem resultado" no seu código.

A política **"aceito clientes que são PEP?"** é sua: por padrão ser PEP **não reprova** (sai como sinalização, que é o que a regulação pede: diligência reforçada, não recusa). Você pode desligá-la no flow, ou por sessão ao criar com `sk_` mandando `{ "policy": { "allow_pep": false } }`. Só o vínculo **atual** reprova; vínculo passado nunca. Chave de política desconhecida é 400 `unknown_policy_key`.

**O resumo pronto de PEP e sanção.** Você não precisa refazer a conta em cada integração. O bloco `pep_status` diz se há função pública **em exercício** (`current`) e se a pessoa **deixou** a função no último ano, nos últimos três ou nos últimos cinco anos (`last_1y`, `last_3y`, `last_5y`), contados do fim do exercício. `roles` traz a descrição da função como a fonte oficial publica, e só quando o casamento foi pelo documento e forte: semelhança de nome nunca traz cargo. Em `aggregates`, `is_pep`, `has_sanctions_br` (lista publicada por órgão brasileiro) e `has_sanctions_intl` (lista publicada por organismo ou governo estrangeiro) resumem os hits, `worst_severity` dá a pior zona entre eles (`no_hit`, `weak` ou `strong`), e `sanction_windows` e `pep_windows` separam as janelas por tipo de lista. `windows` continua no payload como a soma das duas, marcado como depreciado: prefira as separadas.

**Programe contra `source`, não contra `list`.** Em cada hit, `source` é o identificador estável da lista (`cgu_pep`, `cgu_ceis`, `ofac_sdn`) e não muda; `list` é o rótulo legível e pode mudar de texto. Na lista de PEP, `left_at` é o fim do período em que a pessoa é considerada politicamente exposta, e `exercise_end` é o fim do exercício da função; `pep_in_carency` marca quem já deixou a função e ainda está dentro dos cinco anos.

**Cota diária do módulo.** Cada organização tem uma cota diária de sessões novas com este módulo em produção. Passando dela, a criação de sessão responde `429 module_quota_reached` com `Retry-After` até a virada do dia (meia-noite UTC). A renovação de uma sessão não conta de novo. Se o seu volume pede mais, fale com a gente: o ajuste é por organização.

**A lista de hits tem teto, e o corte vem declarado.** Nome comum produz muito candidato, e um payload sem limite viraria incidente de custo e de leitura do seu lado. Passando do teto, `hits` sai cortado e `hits_truncated` vem `true`. Duas garantias tornam o corte seguro de programar: os `aggregates` são calculados sobre o conjunto **completo** (a contagem continua verdadeira mesmo com a lista cortada), e a ordem é determinística **antes** do corte, com hit forte na frente do fraco e casamento por documento na frente do casamento por nome. O que fica de fora é sempre a cauda mais fraca, nunca a evidência que decide.

```
// pep_sancoes: nada consta
{ "module": "pep_sancoes", "passed": true, "outcome": "approved", "score": 95,
  "data": { "pep": false, "flagged": false, "matched_by": "none", "reason": "clean",
            "pep_status": { "current": false, "last_1y": false, "last_3y": false, "last_5y": false,
                            "roles": [] },
            "hits": [], "hits_truncated": false,
            "aggregates": { "total": 0, "pep": 0, "sanctions": 0, "strong": 0,
                            "is_pep": false, "has_sanctions_br": false, "has_sanctions_intl": false,
                            "worst_severity": "no_hit",
                            "pep_windows": { "d30": 0, "d90": 0, "d180": 0, "d365": 0, "d1825": 0, "total": 0 },
                            "sanction_windows": { "d30": 0, "d90": 0, "d180": 0, "d365": 0, "d1825": 0, "total": 0 },
                            "windows": { "d30": 0, "d90": 0, "d180": 0, "d365": 0, "d1825": 0, "total": 0 } },
            "policy": { "allow_pep": true, "source": "flow" },
            "dataset_versions": { "cgu_pep": { "ingested_at": "2026-08-21T03:10:00Z", "age_hours": 9.2,
                                               "record_count": 133880,
                                               "stale": false, "disabled": false } } } }

// pep_sancoes: PEP atual, política do flow permitindo (sinaliza, NÃO reprova)
{ "module": "pep_sancoes", "passed": true, "outcome": "approved", "score": 70,
  "data": { "pep": true, "flagged": true, "matched_by": "document", "reason": "pep_current",
            "pep_status": { "current": true, "last_1y": false, "last_3y": false, "last_5y": false,
                            "roles": [ "DIRETOR" ] },
            "hits": [ { "source": "cgu_pep", "list": "PEP", "matched_by": "document", "strong": true,
                        "similarity": 1, "precision": 1, "listed_at": "2025-02-01", "left_at": null,
                        "current": true, "entry_ref": "cgu_pep:8831",
                        "exercise_end": null, "pep_in_carency": false,
                        "details": { "funcao": "DIRETOR", "orgao": "MINISTERIO X" } } ] } }

// pep_sancoes: deixou a função há dois anos e segue exposta pela carência de cinco anos
{ "module": "pep_sancoes", "passed": true, "outcome": "approved", "score": 70,
  "data": { "pep": true, "flagged": true, "matched_by": "document", "reason": "pep_current",
            "pep_status": { "current": false, "last_1y": false, "last_3y": true, "last_5y": true,
                            "roles": [ "SECRETARIO" ] },
            "hits": [ { "source": "cgu_pep", "list": "PEP", "matched_by": "document", "strong": true,
                        "similarity": 1, "precision": 1, "listed_at": "2019-01-01",
                        "left_at": "2028-12-31", "current": true, "entry_ref": "cgu_pep:9120",
                        "exercise_end": "2023-12-31", "pep_in_carency": true,
                        "details": { "funcao": "SECRETARIO", "orgao": "MINISTERIO X" } } ] } }

// pep_sancoes: semelhança de NOME numa lista internacional (candidato, não veredito)
// repare no que NÃO vem: nome, documento, cargo ou texto livre do terceiro.
{ "module": "pep_sancoes", "passed": true, "outcome": "approved", "score": 60,
  "data": { "pep": false, "flagged": true, "matched_by": "name", "reason": "sanction_name_weak",
            "hits": [ { "source": "ofac_sdn", "list": "OFAC SDN", "matched_by": "name",
                        "strong": false, "similarity": 0.82, "precision": 0.64,
                        "listed_at": "2019-05-10", "left_at": null, "current": true,
                        "entry_ref": "ofac_sdn:4417" } ] } }

// pep_sancoes: lista nossa vencida, pending, review, e NÃO cobrado
// QUAL lista venceu você lê em dataset_versions, na fonte com "stale": true.
{ "module": "pep_sancoes", "passed": null, "outcome": "pending", "score": 0,
  "data": { "pep": false, "flagged": false, "matched_by": "none", "reason": "dataset_stale",
            "hits": [], "hits_truncated": false, "aggregates": null,
            "policy": { "allow_pep": true, "source": "flow" },
            "dataset_versions": { "cgu_pep": { "ingested_at": "2026-08-19T03:10:00Z",
                                               "age_hours": 51.4, "record_count": 133880,
                                               "stale": true, "disabled": false } } } }

// pep_sancoes: nenhuma lista do modulo disponivel, pending, review, e NAO cobrado
// dataset_versions continua vindo inteiro: voce ve exatamente quais listas ficaram de fora.
{ "module": "pep_sancoes", "passed": null, "outcome": "pending", "score": 0,
  "data": { "pep": false, "flagged": false, "matched_by": "none", "reason": "no_coverage",
            "hits": [], "hits_truncated": false, "aggregates": null,
            "policy": { "allow_pep": true, "source": "flow" },
            "dataset_versions": { "cgu_pep": { "ingested_at": "2026-08-21T03:10:00Z",
                                               "age_hours": 9.2, "record_count": 133880,
                                               "stale": false, "disabled": true } } } }

// pep_sancoes: uma lista que a resposta nao pode dispensar esta fora do ar
{ "module": "pep_sancoes", "passed": null, "outcome": "pending", "score": 0,
  "data": { "pep": false, "flagged": false, "matched_by": "none",
            "reason": "critical_source_disabled",
            "hits": [], "hits_truncated": false, "aggregates": null,
            "policy": { "allow_pep": true, "source": "flow" } } }

// pep_sancoes: lista NAO consultada aparece como null em by_list (nunca ausente)
{ "module": "pep_sancoes", "passed": true, "outcome": "approved", "score": 95,
  "data": { "pep": false, "flagged": false, "matched_by": "none", "reason": "clean",
            "aggregates": { "total": 0, "pep": 0, "sanctions": 0, "strong": 0,
                            "by_list": { "TCU": null },
                            "windows": { "d30": 0, "d90": 0, "d180": 0, "d365": 0, "total": 0 } } } }
```

Em sandbox nada é consultado: o desfecho vem do sufixo do documento, como no resto do ambiente de testes. `66` devolve PEP atual com a política recusando, `77` PEP atual com a política permitindo, `44` PEP que deixou a função há dois anos e segue na carência, `88` sanção por documento no CEIS e `99` um homônimo na OFAC com semelhança 0,82. O `pep_status` e os agregados do sandbox saem da mesma conta da produção.
