> ## Documentation Index
> Fetch the complete documentation index at: https://docs.userkit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Termos e aceite

> Os documentos que os seus clientes aceitam, as versões deles, e o registro de quem concordou com quais palavras.

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.

```bash theme={null}
POST /v1/organization/legal?environment=live
{ "slug": "termos-de-uso", "title": "Termos de uso", "enforcement": "required" }
```

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:

```bash theme={null}
POST /v1/organization/legal/{id}/versions
{ "body_markdown": "# Termos de uso\n\n...", "summary": "", "requires_reacceptance": true }
```

E publicar é um ato à parte, porque é o momento em que as palavras passam a valer
para alguém:

```bash theme={null}
POST /v1/organization/legal/{id}/versions/{versionId}/publish
{ "effective_at": "2026-03-15T00:00:00-03:00" }
```

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

```ts theme={null}
const documents = await userkit.legal.listDocuments();
const terms = await userkit.legal.getDocument("termos-de-uso"); // com o markdown
```

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:

```tsx theme={null}
import { Agreements } from "@userkit/react";

<Agreements onAccepted={() => router.refresh()} />;
```

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](/pt-br/guides/surveys).

Num app Next, importe do `@userkit/nextjs` — é o mesmo componente, e em
[modo proxy](/pt-br/customer-auth/session-tokens) 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:

```ts theme={null}
const agreements = await userkit.legal.listAgreements();
await userkit.legal.accept(agreements[0].version_id);
```

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:

```bash theme={null}
POST /v1/contacts/{id}/legal-acceptances
{ "version_id": "…" }
```

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.
