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

# Termos do programa

> Versionamento e aceite de termos por programa.

Os termos são o acordo comercial de cada [Programa](/docs/conceitos/programas-e-regras): o documento que o [Afiliado](/docs/conceitos/afiliados) precisa aceitar para participar. Cada publicação cria uma nova versão numerada e imutável, formando uma trilha de auditoria de "qual texto o afiliado aceitou e quando".

O versionamento é **por programa**: cada programa de uma organização tem sua própria sequência de versões, começando em `1`.

<Note>
  O termo é um **documento de texto livre** (`content`). Percentuais e valores citados no texto são informativos: eles **não** são extraídos nem usados no cálculo de [Comissões](/docs/conceitos/comissoes). Os parâmetros de comissionamento (percentuais, valores fixos, vigência por data) vivem nas regras de comissão do programa. O elo entre os termos e o resto da plataforma é o **aceite**.
</Note>

## A entidade Termo

Um termo (`term_...`) é uma versão publicada do acordo de um programa:

<ResponseField name="id" type="string">
  Identificador com prefixo de tipo: `term_` + ULID de 26 caracteres.
</ResponseField>

<ResponseField name="organizationId" type="string">
  Organização (tenant) dona do termo.
</ResponseField>

<ResponseField name="programId" type="string">
  Programa dono do termo (`prog_...`). O versionamento é por programa.
</ResponseField>

<ResponseField name="version" type="integer">
  Número da versão, sequencial a partir de `1`. Atribuído automaticamente pelo servidor como a próxima versão do programa (a maior versão existente mais um). Único por programa.
</ResponseField>

<ResponseField name="content" type="string">
  O texto do acordo. Mínimo de 1 caractere, sem limite máximo e sem estrutura.
</ResponseField>

<ResponseField name="requireReacceptance" type="boolean">
  Default `false`. Sinaliza que esta versão deveria exigir re-aceite dos afiliados já cadastrados. A flag é armazenada, devolvida e incluída no evento `terms.published`, útil para que sua integração decida notificar os afiliados a re-aceitar. A plataforma não força o re-aceite automaticamente.
</ResponseField>

<ResponseField name="createdAt" type="string">
  Momento da publicação (timestamp ISO 8601).
</ResponseField>

O **aceite** não é uma entidade própria: é o campo `acceptedTermsVersion` (inteiro, anulável) no afiliado. Um afiliado recém-criado nasce com `acceptedTermsVersion = null`.

## Ciclo de vida

O termo não tem um campo de status: o estado é **derivado** da numeração. A versão de maior número do programa é a vigente. Versões antigas continuam consultáveis e aceitáveis para sempre.

```mermaid theme={null}
stateDiagram-v2
    [*] --> Vigente: POST /programs/{programId}/terms
    Vigente --> Substituida: publicação de uma versão mais nova
    note right of Vigente
        Derivado: versão com o maior
        "version" do programa.
    end note
    note right of Substituida
        Permanece consultável e aceitável
        (imutável, sem delete).
    end note
```

* **Publicação**: `POST /programs/{programId}/terms` cria a próxima versão e emite o evento `terms.published`.
* **Substituição**: acontece implicitamente quando uma versão mais nova é publicada. Não há transição explícita, evento próprio nem alteração na versão antiga.
* Não existem operações de edição, despublicação ou exclusão. Mudar o acordo significa publicar uma nova versão.

## Publicar uma nova versão

`POST /programs/{programId}/terms` exige papel **owner** ou **admin**. O corpo aceita `content` (obrigatório, mínimo 1 caractere) e `requireReacceptance` (opcional, default `false`). O endpoint suporta [Idempotência](/docs/convencoes/idempotencia) via header `Idempotency-Key`.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.userepass.com/programs/prog_01J9Z3K7QewQ4M2N5P8R0T2V4X/terms \
    -X POST \
    -H 'Authorization: Bearer rstr_...' \
    -H 'Content-Type: application/json' \
    -H 'Idempotency-Key: 0f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f' \
    -d '{
      "content": "Acordo de afiliação v3. Condições de divulgação, vigência e comissionamento...",
      "requireReacceptance": true
    }'
  ```

  ```json Resposta 201 theme={null}
  {
    "id": "term_01J9ZB4M6N8P0Q2R4S6T8V0W2Y",
    "organizationId": "org_01J9Z0A1B2C3D4E5F6G7H8J9K0",
    "programId": "prog_01J9Z3K7QewQ4M2N5P8R0T2V4X",
    "version": 3,
    "content": "Acordo de afiliação v3. Condições de divulgação, vigência e comissionamento...",
    "requireReacceptance": true,
    "createdAt": "2026-06-13T14:32:08.512Z"
  }
  ```
</CodeGroup>

A numeração é por programa e independente: publicar no programa A não afeta a sequência do programa B, mesmo dentro da mesma organização.

<AccordionGroup>
  <Accordion title="Erros de POST /programs/{programId}/terms">
    * `400 parameter_invalid`: corpo inválido (ex.: `content` vazio ou ausente) ou `programId` mal formado.
    * `401 unauthorized`: sem sessão ou API key válida.
    * `403 not_allowed`: membro sem papel owner/admin.
    * `404 resource_not_found`: programa inexistente na organização.
    * `409 idempotency_in_flight`: replay enquanto a requisição original ainda processa.
    * `400 idempotency_key_reused`: mesma `Idempotency-Key` com payload diferente.

    Consulte [Erros](/docs/convencoes/erros) para o formato do envelope.
  </Accordion>
</AccordionGroup>

Para a referência completa do endpoint, veja a aba **Referência da API**.

## Listar versões

`GET /programs/{programId}/terms` está disponível para qualquer membro da organização. Diferente das demais listagens da API, este endpoint **não usa [paginação](/docs/convencoes/paginacao) por cursor nem [expand](/docs/convencoes/expand)**: devolve todas as versões do programa em `data`, ordenadas da mais nova para a mais antiga (`version` decrescente). O volume baixo de versões por programa torna a paginação desnecessária.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.userepass.com/programs/prog_01J9Z3K7QewQ4M2N5P8R0T2V4X/terms \
    -H 'Authorization: Bearer rstr_...'
  ```

  ```json Resposta 200 theme={null}
  {
    "data": [
      {
        "id": "term_01J9ZB4M6N8P0Q2R4S6T8V0W2Y",
        "organizationId": "org_01J9Z0A1B2C3D4E5F6G7H8J9K0",
        "programId": "prog_01J9Z3K7QewQ4M2N5P8R0T2V4X",
        "version": 3,
        "content": "Acordo de afiliação v3...",
        "requireReacceptance": true,
        "createdAt": "2026-06-13T14:32:08.512Z"
      },
      {
        "id": "term_01J9Z7G2H4J6K8M0N2P4Q6R8S0",
        "organizationId": "org_01J9Z0A1B2C3D4E5F6G7H8J9K0",
        "programId": "prog_01J9Z3K7QewQ4M2N5P8R0T2V4X",
        "version": 2,
        "content": "Acordo de afiliação v2...",
        "requireReacceptance": false,
        "createdAt": "2026-05-01T09:10:00.000Z"
      }
    ]
  }
  ```
</CodeGroup>

<AccordionGroup>
  <Accordion title="Erros de GET /programs/{programId}/terms">
    * `400 parameter_invalid`: `programId` mal formado (deve casar `prog_` + ULID).
    * `401 unauthorized`: sem sessão ou API key válida.
    * `404 resource_not_found`: programa inexistente na organização.
  </Accordion>
</AccordionGroup>

## Biblioteca de templates

Para não escrever o acordo do zero, `GET /terms/templates` devolve um catálogo de **modelos de termos em Markdown curados pelo Repass**, disponível para qualquer membro autenticado. Cada template traz `variables` (placeholders `{{chave}}` com rótulo, exemplo e valor sugerido) e o `content` integral: preencha as variáveis, substitua os tokens e use o resultado como `content` na publicação normal.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.userepass.com/terms/templates \
    -H 'Authorization: Bearer rstr_...'
  ```

  ```json Resposta 200 (resumida) theme={null}
  {
    "data": [
      {
        "id": "saas-subscription",
        "name": "SaaS / assinatura",
        "description": "Acordo para produtos por assinatura, com comissão recorrente...",
        "category": "saas",
        "variables": [
          {
            "key": "nome_empresa",
            "label": "Nome da empresa",
            "description": "Razão social completa, como consta no contrato social.",
            "example": "Acme Software Ltda",
            "required": true
          }
        ],
        "content": "# Termos do Programa de Afiliados de {{produto}}..."
      }
    ]
  }
  ```
</CodeGroup>

<Note>
  O template é um ponto de partida, não um vínculo: o texto aplicado vira o `content` de uma versão publicada normalmente (snapshot imutável). Revisões futuras do catálogo não alteram termos já publicados ou aceitos, e nenhum `templateId` é gravado na versão.
</Note>

## Aceite pelo afiliado

O aceite registra qual versão o afiliado concordou. A plataforma valida que o afiliado existe e que a versão informada existe **no programa do afiliado**, grava `acceptedTermsVersion` e emite o evento `affiliate.terms_accepted` (com `programId` e `termsVersion`).

```mermaid theme={null}
sequenceDiagram
    participant Afiliado
    participant Plataforma

    Afiliado->>Plataforma: aceita a versão (termsVersion)
    Plataforma->>Plataforma: valida que o afiliado existe (ou 404)
    Plataforma->>Plataforma: valida a versão no programa do afiliado (ou 404, param "termsVersion")
    Plataforma->>Plataforma: grava acceptedTermsVersion + evento affiliate.terms_accepted
    Plataforma-->>Afiliado: afiliado atualizado
```

Pontos importantes do aceite:

* O aceite resolve a versão **dentro do programa do afiliado**. Aceitar uma versão que não existe naquele programa retorna `404 resource_not_found` com o parâmetro `termsVersion`.
* O aceite aponta para **qualquer versão publicada** do programa, não apenas a vigente. É possível (e permitido) aceitar uma versão antiga, inclusive regredindo `acceptedTermsVersion`: não há validação de ordem.

<Warning>
  A flag `requireReacceptance` é informativa: ela é persistida, devolvida e emitida no evento `terms.published`, mas a plataforma não compara `acceptedTermsVersion` com a versão vigente para forçar re-aceite automaticamente. Use o evento para conduzir o re-aceite na sua própria experiência.
</Warning>

## Eventos emitidos

Toda mudança de estado emite um evento no [Event Store](/docs/conceitos/event-store), disponível também para [Webhooks](/docs/webhooks/visao-geral).

| Tipo                       | Quando                               | Payload                                                                                                                             |
| -------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `terms.published`          | Publicação de uma nova versão.       | `{ terms }` com a entidade completa: `id`, `organizationId`, `programId`, `version`, `content`, `requireReacceptance`, `createdAt`. |
| `affiliate.terms_accepted` | Quando o afiliado aceita uma versão. | `programId` e `termsVersion` (número da versão aceita).                                                                             |

## Próximos passos

<CardGroup cols={2}>
  <Card title="Programas e regras" icon="layer-group" href="/docs/conceitos/programas-e-regras">
    Os parâmetros de comissionamento que os termos não computam.
  </Card>

  <Card title="Afiliados" icon="user-group" href="/docs/conceitos/afiliados">
    Onde mora o campo acceptedTermsVersion.
  </Card>

  <Card title="Event Store" icon="database" href="/docs/conceitos/event-store">
    Consulte terms.published e affiliate.terms\_accepted.
  </Card>

  <Card title="Idempotência" icon="rotate" href="/docs/convencoes/idempotencia">
    Como evitar publicações duplicadas no POST.
  </Card>
</CardGroup>
