Criar conta grátis

Documentação
Ver em Markdown

Atestado de pessoa verificada

Atestado de pessoa verificada

O módulo atestado_humano 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.

!Em breve. A venda deste módulo está pausada: ele aparece na tabela de preços com o selo "Em breve", e o create de flow o recusa até a abertura. O que está descrito abaixo é o contrato que o backend já emite, para você planejar a integração.

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/pessoa-verificada/v1",
  "kid": "prd-0123456789ab",
  "issued_at": "2026-09-26T00:00:00Z",
  "expires_at": "2027-03-25T00:00:00Z",
  "disclosable": ["sub", "prova_de_vida", "unica_no_servico", "maioridade"] }

// 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/pessoa-verificada/v1",
  "issued_at": "2026-09-26T00:00:00Z",
  "expires_at": "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 atestado_humano), 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, baixe uma emissão nova pelo painel quando a emissão voltar.

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

// atestado_humano no check_details: não emitido (aprovação manual)
{ "module": "atestado_humano", "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) e attestation_issue_failed (a emissão falhou e a verificação seguiu sem ela). 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.

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
"prova_de_vida": { "resultado": true, "data": "2026-09-26" }
"unica_no_servico": true                  // só quando o flow tem face_unica e nenhuma outra conta foi encontrada
"maioridade": { "resultado": true, "metodo": "documento" }   // 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 unica_no_servico quer dizer "sem outra conta encontrada neste serviço", e só aparece quando o módulo Detecção de múltiplas contas foi contratado no flow. A maioridade 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 maioridade. A data de nascimento em si nunca entra.

Validade. 180 dias. O iat e o exp são arredondados ao começo do dia em UTC, para o horário exato da verificação não ficar gravado no atestado.

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 (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, ["prova_de_vida"]) no TypeScript, ou present_human_attestation(sd_jwt, ["prova_de_vida"]) 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 e as mesmas informações, então dois downloads não são o mesmo token.

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.

Pronto para integrar? A chave de sandbox sai no painel, logo depois do cadastro. Criar conta grátis