Skip to main content
Todo produto uma hora precisa responder a uma pergunta sobre uma pessoa: ela aceitou, e o que exatamente? Este é o módulo que responde — termos de uso, política de privacidade, código de conduta, adendo de tratamento de dados, e o registro de aceite por trás de cada um. Ele é pequeno de propósito. Um documento, as versões dele, e uma linha por pessoa por versão. Todo o resto deste guia é consequência de uma decisão: o aceite aponta para uma versão, nunca para um documento.

Por que a versão, e não o documento

Um registro dizendo “a Ana aceitou os seus termos” não vale nada no dia em que os termos mudam. Ele prova que alguém marcou uma caixinha ao lado do que o texto diz hoje — que é o contrário do motivo pelo qual o registro é guardado. Então as palavras moram numa versão, e publicar congela. O corpo, o resumo, a data de vigência e a regra de reaceite viram imutáveis — garantido por um trigger no banco, não por convenção — e corrigir uma vírgula é publicar outra versão, do mesmo jeito que remarcar um preço é escrever um preço novo. O aceite guarda o id da versão e o número dela, junto com o momento, o endereço de onde veio o clique e o navegador em que foi feito.

Publicando

O documento é criado sem texto nenhum: um nome, um endereço (slug) e a regra sobre o aceite ser obrigatório. Criar um nunca coloca nada na frente de ninguém.
As palavras são uma versão, escrita como rascunho — e há um rascunho por vez por documento, o que é regra do banco e não da tela:
E publicar é um ato à parte, porque é o momento em que as palavras passam a valer para alguém:
effective_at pode estar no futuro, que é o que um aviso prévio é: até essa data a versão anterior continua em vigor, e continua sendo a oferecida para aceite. Não pode estar no passado — datar termos para trás seria afirmar que alguém foi regido por palavras que não existiam. Publicar acontece exatamente uma vez por versão. A segunda chamada responde 404, porque um segundo publish moveria a data de vigência de termos que gente já aceitou.

Avisando

notify: true no publish coloca uma notificação action no sino de todo mundo que a publicação põe diante de um checkbox — e só deles, calculado com o mesmo piso que o card usa. Vem desligado, porque uma versão publicada para consertar um título não pode virar cobrança na notificação de ninguém. Com effective_at no futuro, o aviso espera a data. Ele sai no dia em que os termos passam a valer, para quem devê-los naquele dia. Avisar na publicação alcançaria quem deve a versão anterior, sobre palavras que ainda não regem ninguém — e o dia da vigência passaria sem acontecer nada. Uma varredura noturna envia, então o aviso chega dentro de um dia da vigência, não no segundo dela.

Quem é perguntado de novo, e quem não é

requires_reacceptance é o campo de maior alcance aqui, e o padrão é true. Cada documento tem um piso: a maior versão em vigor que declarou mudança material. A pessoa está em dia quando aceitou qualquer versão igual ou acima dele.
  • Publique um link corrigido com requires_reacceptance: false — ninguém é perguntado de novo, e todo aceite já registrado continua valendo.
  • Publique uma cláusula de arbitragem com requires_reacceptance: true — todo mundo abaixo do piso volta a ficar pendente, na mesma instrução, sem nada para reprocessar.
O padrão é true por causa do que cada erro custa. Perguntar de novo sem precisar custa um clique. Não perguntar quando devia é uma mudança de contrato que ninguém foi avisado — que é exatamente o que este módulo existe para impedir.

O que required significa, e o que não significa

enforcement: "required" diz que ninguém usa o produto aqui sem ter aceitado. O UserKit não é dono de nenhuma porta em que uma conta nasce — o seu produto é —, então ele nunca recusa ninguém. O que ele faz é avisar: o aceite é blocking em vez de só oferecido, e toda leitura diz isso. GET /v1/contact/me carrega pending_agreements, GET /v1/contact/legal carrega o quadro inteiro, e cada pendência diz blocking: true. Nada recusa uma chamada comum por causa de um aceite pendente, e POST /v1/contact/token em particular nunca recusa. Essa última é a razão de toda a posição. O token é o fluxo de refresh: barrar ali significaria que publicar termos novos desloga todos os seus clientes do seu produto, de uma vez, por causa de uma caixinha. Publicar termos tem que significar “todo mundo é perguntado”, nunca “todo mundo é expulso” — então o que fazer com uma pendência é decisão do seu produto, tomada onde dá para ver o que interromper custa.

Lendo os documentos

GET /v1/legal/{publishable_key} é público e cacheável, endereçado pela chave que já está na sua página. E tem que ser: o seu formulário de cadastro desenha o link dos termos antes de existir qualquer sessão, e um formulário que não consegue mostrar o que precisa mostrar é um formulário que não pode aceitar cadastro.
Rascunhos não aparecem, documentos arquivados não aparecem, e uma versão publicada para o mês que vem não aparece até a data dela. Renderize o markdown — nunca jogue em innerHTML: é prosa vinda de um banco desenhando na sua página.

Perguntando, dentro do seu produto

A caixinha do cadastro fica no seu próprio formulário, e o seu backend registra o aceite (abaixo). Depois disso, o <Agreements /> desenha o que está pendente e nada no caso comum, que é o que o torna seguro de montar de forma permanente:
Com position="center" ele é um modal, e aí entra numa fila: o centro da página comporta um diálogo por vez, e este é o primeiro dela — a condição que você declarou vem antes de uma novidade publicada e de uma pesquisa, que esperam a vez. A ordem, e como trocá-la, estão em Pesquisas. Num app Next, importe do @userkit/nextjs — é o mesmo componente, e em modo proxy tanto a leitura quanto o aceite são encaminhados pelos handlers, com a sessão num cookie httpOnly da sua própria origem. O useLegalDocuments atravessa junto e continua sem exigir sessão: a rota pública dos documentos está na allowlist sem authenticated, porque o seu formulário de cadastro precisa desenhar o link antes de existir sessão. Ele lê a lista de pendências do estado da sessão em vez de ficar consultando, então uma versão publicada hoje de manhã aparece sem requisição própria. Documentos opcionais ficam de fora a menos que você peça (includeOptional) — um documento opcional é uma oferta, e interromper alguém com um faria a palavra não significar nada. Dirigindo na mão são duas chamadas:
Aceitar duas vezes é aceitar uma. As duas chamadas respondem 200 e recorded diz qual delas gravou a linha; o horário e a origem continuam sendo os do primeiro aceite. Uma versão que saiu de vigor responde 404, que é como uma página deixada aberta a noite toda falha — releia em vez de tentar de novo.

Registrando o aceite pelo seu backend

O formulário de cadastro é seu, então a caixinha é marcada no seu app e o seu backend nos conta:
A linha fica marcada com source: api, para sempre. São os aceites cuja evidência é sua e não nossa, e a pergunta “como você sabe que essa pessoa aceitou” tem outra resposta para eles.

O registro

GET /v1/organization/legal/{id}/acceptances é a lista de evidência: quem aceitou, qual versão, quando, de onde e por qual porta. Um aceite sobrevive ao apagamento da pessoa que ele nomeia. Isso é proposital: ele é o seu registro da base legal sobre a qual você tratou alguém, e uma pessoa exercendo o direito ao apagamento não desfaz com isso o aceite dos termos sob os quais foi atendida. O que o apagamento leva é o par que é sobre a pessoa e não sobre o acordo — o endereço e o navegador —, deixando ids, um número de versão e um momento. A lista mostra contact: null nessas linhas, que é a cara que o registro deve ter depois. Pelo mesmo motivo, um documento que já publicou alguma coisa não pode ser excluído. Tirar termos do ar é PATCH { "archived": true }: ele sai do documento público e deixa de ser pedido a qualquer pessoa, e todos os aceites continuam lá.

Eventos

legal_document.published dispara uma vez por versão e é entregável nos seus webhooks — invalide a página de termos cacheada no seu site, mande o aviso, reabra o portão dentro do seu produto. legal_agreement.accepted dispara no primeiro aceite de uma versão por uma pessoa, que é o momento em que um portão em outro lugar pode abrir. Os fatos do rascunho (legal_document.updated) ficam dentro: uma versão não publicada é uma posição jurídica que você ainda não tomou, e entregabilidade é uma porta de mão única.