SuperdindaDocs

Consentimento

A atestação do responsável, o link de consentimento, a página pública, a evidência por selfie e áudio, a recusa e a parada pela própria pessoa.

A proteção só liga com o sim da própria pessoa (AD-009). São duas camadas:

  • Atestação do responsável: a base legal, gravada na ativação (guardianAttestation: true).
  • Consentimento da pessoa: ela abre um link, lê o que será acessado e confirma com uma selfie e um áudio lendo a frase do termo.

Ao lado corre a checagem de vínculo, que confere a relação declarada pelo documento. Só refuted bloqueia. Hoje não há provedor ligado, e toda checagem sai inconclusive (vai pra revisão manual).

Lado do responsável

AçãoRota
Pedir o sim (vaga pendente, antes do cartão)POST /seats/{uid}/consent-request com { send: "now" | "later" }. Veja Onboarding
Ver o estadoGET /seats/{uid}/consent: o consentimento, a evidência recebida (só metadados, nunca os arquivos), a checagem de vínculo, pairingAllowed e blockedBy
Mandar um link novoPOST /seats/{uid}/consent-request/resend. O anterior para de funcionar
Retirar o pedido pendenteDELETE /seats/{uid}/consent-request. Não desfaz um sim já dado

O link vai por WhatsApp quando o telefone começa com +55, e por SMS nos outros casos. Vale CONSENT_TTL_HOURS (padrão 72 h). O objeto consent da vaga traz a linha do tempo da entrega: sentAt, deliveredAt (recibo da Meta), openedAt, declinedAt, resendAvailableAt, revokedAt e revokedBy. link aparece só enquanto o pedido está pending.

Status do pedido: pending, given, declined, expired ou revoked.

Lado da pessoa (rotas públicas pelo token)

  1. Abrir o link. GET /consent-requests/{token} devolve quem pede, o nome da família, o que será acessado (scope), a frase exata pra ler (congelada nesse pedido, com phraseVersion e termsVersion), o bloco privacy e protection (not_started, active, paused ou stopped). A primeira abertura gera consent.opened pros responsáveis.

  2. Dar o sim. POST /consent-requests/{token}/evidence, em multipart/form-data:

    CampoRegra
    selfieimage/jpeg, png ou webp, até 8 MB. Exatamente um rosto
    audioaudio/webm, mp4, ogg, mpeg ou wav (aceita video/webm), até 20 MB. Precisa bater com a frase
    accepted"true"
    audioDurationMsOpcional

    A verificação roda antes de guardar qualquer coisa. Só depois de aprovada os arquivos são gravados e o pedido vira given (201). A resposta traz protectionLink, o link de "Minha proteção" da própria pessoa. Ele aparece uma vez, só na tela dela, e nunca em resposta pro responsável.

  3. Ou recusar. POST /consent-requests/{token}/decline com { choice: "not_now" | "talk_first" }. A escolha fica no servidor: o responsável recebe só o fato e a hora (consent.declined), nunca o motivo. Repetir a recusa devolve a primeira. Depois de uma recusa, o resend fica travado por CONSENT_RESEND_COOLDOWN_HOURS (padrão 24 h, RESEND_LOCKED).

Erros da evidência: AUDIO_PHRASE_MISMATCH (com expected, heard, missing, score), AUDIO_UNINTELLIGIBLE, SELFIE_NO_FACE, CONSENT_EXPIRED (422), 409 se o sim já foi dado, 503 EVIDENCE_VERIFICATION_UNAVAILABLE se o verificador estiver fora. Todas as rotas públicas têm limite de taxa.

Minha proteção e a parada

As duas rotas aceitam o link de consentimento ou o link de proteção. Quando um consentimento mais novo é dado, os links antigos param (422 PROTECTION_LINK_REPLACED). Um link cujo pedido nunca recebeu o sim responde 422 CONSENT_NOT_GIVEN.

Biometria e retenção (D6)

  • Toda chamada ao verificador (OpenRouter) pede data_collection: "deny" e zdr: true. O provedor não guarda nem treina com a selfie e o áudio.
  • Os arquivos ficam no storage (S3/MinIO) enquanto o consentimento vale, porque são a prova.
  • Eles são apagados EVIDENCE_RETENTION_DAYS (padrão 180) depois que o consentimento termina: parada pela pessoa, troca por um consentimento mais novo ou vaga arquivada. O worker evidence-purger roda de hora em hora. A linha em consent_evidence fica com tipo, tamanho, sha256 e purged_at.
Enviando a evidência (página pública)
const form = new FormData();
form.append("selfie", selfieBlob, "selfie.jpg");
form.append("audio", audioBlob, "voz.webm");
form.append("accepted", "true");
const res = await fetch(`${API}/consent-requests/${token}/evidence`, {
  method: "POST",
  body: form,
});
const body = await res.json();
if (body.success)
  showDone(body.data.protectionLink); // mostrar só pra pessoa
else if (body.error.code === "AUDIO_PHRASE_MISMATCH")
  highlight(body.error.details.missing);

Em aberto

  • Ainda não existe provedor de checagem de vínculo: todo resultado é inconclusive, e a revisão manual não tem tela.
  • Nenhum template de WhatsApp leva o protectionLink (G-37). Se a pessoa perder o link, não há como reenviá-lo hoje.
  • A renovação do consentimento com diff do termo (G-18) e a frase v2 sem CPF (G-12, regra P6) ainda não estão na main. A página pública ainda devolve dependent.documentMasked.

Fontes

  • backend/src/http/routes/consents/page.route.ts, evidence.route.ts, decline.route.ts, protection.route.ts, schemas.ts
  • backend/src/http/routes/seats/consent-request.route.ts, consent-status.route.ts, activate.route.ts
  • backend/src/domains/consents/use-cases/request-consent.ts, submit-evidence.ts, decline-consent.ts, protection.ts, purge-evidence.ts
  • backend/src/domains/consents/checks/registry.ts, verification/errors.ts
  • backend/apps/workers/evidence-purger.ts, backend/src/config/env.ts
  • Notas da frente k-consent (k-consent-api.md)
  • .specs/features/redesign/api-gaps.md (G-12, G-16, G-17, G-18, G-37), api-map.md (§0.2)
  • AD-009, AD-011 (verificação de evidência), AD-019 (D6, D7)

On this page