> ## 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.

# Atestado de pessoa verificada

> O módulo human_attestation entrega, junto com a verificação aprovada, uma declaração assinada que a sua plataforma confere e apresenta sem consultar a UNIFOKAL e sem dado pessoal direto.

## Atestado de pessoa verificada

O módulo `human_attestation` entrega, junto com a verificação aprovada, uma declaração assinada pela UNIFOKAL de que a pessoa passou pela prova de vida. A sua plataforma pode mostrar essa declaração a um auditor, a um regulador ou a um parceiro, e quem recebe confere a assinatura sem consultar a UNIFOKAL e sem receber nome, CPF, rosto ou data de nascimento. O formato é o SD-JWT VC (RFC 9901 com o perfil de credencial verificável da IETF), e cada informação dentro dele pode ser mostrada ou escondida separadamente.

**Quando ele é emitido.** O módulo depende de `liveness` no mesmo flow e só emite em produção, quando a verificação é aprovada pelo caminho automático. Aprovação manual na fila de revisão não emite, e o sandbox nunca emite. A cobrança acontece só quando o atestado sai: não emitido não é cobrado.

**O que chega no webhook.** O corpo do evento ganha `data.attestation`, no nível de cima de `data`, inclusive no modo de payload `minimal`. É ali que vem o token compacto, com o que você precisa para guardá-lo sem abri-lo. São três formas:

```
// data.attestation: emitido
"attestation": {
  "issued": true,
  "id": "hat_01J9ZC6Q8R3M2V7K4T5N1B0XHD",
  "format": "dc+sd-jwt",
  "sd_jwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImRjK3NkLWp3dCIsImtpZCI6InByZC0uLi4ifQ.eyJ...~WyJ...~WyJ...~",
  "vct": "https://unifokal.com/vct/verified-person/v1",
  "kid": "prd-0123456789ab",
  "issued_at": "2026-09-26T15:00:00Z",
  "expires_at": "2026-09-27T15:00:00Z",
  "reissue_until": "2027-03-25T00:00:00Z",
  "disclosable": ["sub", "liveness", "unique_in_service", "legal_age"] }

// data.attestation: emitido, mas o token não pôde ser montado na entrega
"attestation": {
  "issued": true,
  "id": "hat_01J9ZC6Q8R3M2V7K4T5N1B0XHD",
  "format": "dc+sd-jwt",
  "sd_jwt": null,
  "reason": "attestation_signer_not_configured",
  "vct": "https://unifokal.com/vct/verified-person/v1",
  "reissue_until": "2027-03-25T00:00:00Z" }

// data.attestation: não emitido, com o motivo
"attestation": { "issued": false, "reason": "manual_decision" }
```

No bloco do módulo em `check_details` (o item com `module` igual a `human_attestation`), o campo `data.attestation` traz os mesmos dados **sem o token**: nada de `sd_jwt` ali. Quando o atestado foi emitido mas chegou com `sd_jwt` nulo, peça uma emissão nova pelo replay do webhook ou pelo painel quando a emissão voltar.

```
// human_attestation no check_details: emitido (os dados, sem o token)
{ "module": "human_attestation", "passed": true, "outcome": "approved", "score": 100,
  "data": { "attestation": { "issued": true, "id": "hat_01J9ZC6Q8R3M2V7K4T5N1B0XHD",
            "format": "dc+sd-jwt",
            "vct": "https://unifokal.com/vct/verified-person/v1",
            "kid": "prd-0a1b2c3d4e5f",
            "issued_at": "2026-09-26T00:00:00.000Z",
            "reissue_until": "2027-03-25T00:00:00.000Z",
            "disclosable": ["sub", "liveness", "unique_in_service"] } } }

// human_attestation no check_details: não emitido (aprovação manual)
{ "module": "human_attestation", "passed": null, "outcome": "pending", "score": 0,
  "data": { "attestation": { "issued": false, "reason": "manual_decision" } } }
```

Os motivos de não emissão em `reason`: `verification_not_approved` (a verificação não foi aprovada automaticamente), `manual_decision` (a aprovação veio da revisão manual), `attestation_sandbox_not_issued` (sessão de sandbox), `liveness_not_approved` (a prova de vida não aprovou), `attestation_signer_not_configured` (a emissão está temporariamente indisponível do nosso lado), `attestation_issue_failed` (a emissão falhou e a verificação seguiu sem ela) e `attestation_window_closed` (a decisão chegou depois de passados os 180 dias contados do dia da verificação, e não há janela para emitir). Com `issued` igual a `false` não há cobrança do módulo. O `attestation_signer_not_configured` também aparece com `issued` igual a `true` e `sd_jwt` nulo, a segunda forma acima, assim como `attestation_window_closed` (passaram os 180 dias da verificação e nenhum token novo sai).

**O que vai dentro.** Sempre visíveis: o emissor `iss` (`https://unifokal.com`), o tipo `vct` (o mesmo para todo cliente, sem nome de cliente, país ou produto) e a validade em `iat` e `exp`. Cada uma das informações abaixo é divulgável em separado, e a lista `disclosable` diz quais este atestado carrega:

```
"sub": "Y2Vu...43 caracteres"          // identificador aleatório da conta, só neste serviço
"liveness": { "result": true, "date": "2026-09-26" }
"unique_in_service": true                  // só quando o flow tem unique_face e nenhuma outra conta foi encontrada
"legal_age": { "result": true, "method": "document" }   // só com documento lido e rosto aprovado
```

O `sub` é um identificador aleatório, estável para a mesma conta (a sua organização, o ambiente e o `reference_id`) até o pedido de eliminação do titular, e nunca derivado de documento ou de biometria. Dois serviços diferentes recebem identificadores diferentes para a mesma pessoa. A `unique_in_service` quer dizer "sem outra conta encontrada neste serviço", e só aparece quando o módulo [Detecção de múltiplas contas](https://docs.unifokal.com/modulos/face-unica.md#modulo-face-unica) foi contratado no flow. A `legal_age` sai da data de nascimento lida do documento, comparada no dia da emissão, e só aparece quando o flow leu documento com data de nascimento e o rosto bateu com ele. Idade estimada pela selfie nunca vira `legal_age`. A data de nascimento em si nunca entra.

**Validade.** Cada atestado vale até 24 horas. Peça outro pelo replay do webhook (`POST /v1/webhook-events/replay` com o `verification_id`) ou pelo painel por até 180 dias desde o dia da verificação (em UTC), o prazo que vem em `reissue_until`, que nunca passa do prazo do corpo guardado do webhook. Cada token novo tem sal e assinatura novos e as mesmas informações. O `iat` é arredondado ao começo da hora em UTC, para o horário exato da emissão não ficar gravado no atestado, e o `exp` nunca passa de `reissue_until`. O atestado não tem vínculo com uma chave da pessoa: quem tiver o token pode apresentá-lo até ele vencer, por isso a validade é curta. Os SDKs recusam token com validade acima de 24 horas (`validity_too_long`).

**Como conferir sem nos consultar.** O cabeçalho traz `alg` `ES256`, `typ` `dc+sd-jwt` e o `kid` da chave. As chaves públicas de produção vêm fixadas nos SDKs e também estão publicadas em [https://unifokal.com/.well-known/jwt-vc-issuer](https://unifokal.com/.well-known/jwt-vc-issuer) (o documento de metadado do emissor do SD-JWT VC). No SDK TypeScript, `verifyHumanAttestation(sdJwt)`; no SDK Python, `verify_human_attestation(sd_jwt)`. Os dois conferem a assinatura, o emissor, o tipo, a validade e cada divulgação, e recusam por padrão qualquer `kid` que não seja de produção.

**Como mostrar só uma parte.** `presentHumanAttestation(sdJwt, ["liveness"])` no TypeScript, ou `present_human_attestation(sd_jwt, ["liveness"])` no Python, devolve o mesmo token só com as divulgações escolhidas. A assinatura continua valendo, e quem recebe não vê as outras informações.

**Baixar pelo painel.** No detalhe da verificação, quem é dono ou administrador da conta baixa o atestado. Cada download é uma emissão nova, com sal e assinatura novos, as mesmas informações e validade de até 24 horas, então dois downloads não são o mesmo token. Passados os 180 dias, o download responde `422 attestation_window_closed`, e o mesmo vale para o replay.

**O que ele não é.** O atestado não esconde a pessoa da UNIFOKAL: como emissora, a UNIFOKAL consegue ligar um atestado à verificação que o originou. O que ele garante é que dois serviços que recebem atestados não conseguem ligar as contas entre si por meio dele. Esta versão não tem vínculo de chave: quem tem o token consegue apresentá-lo, então guarde-o como guarda uma credencial. Nada emitido em sandbox, nem os vetores de teste dos SDKs, confere contra as chaves publicadas. E o atestado diz o que a verificação encontrou no dia, sem acompanhar o que acontece com a conta depois.


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