---
title: Consents & legal documents
description: Consent Registry, signup and paywall checkboxes, site forms, mailing list.
---

# Consents & legal documents

The platform stores **evidence of consent**: which document and revision were accepted, when, from which IP/platform, with which checkbox label.

## Where to author texts

| Owner | Admin | Public API |
|-------|-------|------------|
| **Application** | Applications → Legal documents | `GET /v1/applications/YOUR_APP_CODE/legal` |
| **CMS site** | CMS Site → Legal documents | `GET /v1/sites/YOUR_SITE_CODE/legal` |

CMS **Documents** is for arbitrary landing files/links only — not legal templates.

`doc_kind` slots: `privacy_policy`, `personal_data_consent`, `marketing_consent`, `mobile_apps_terms`, `terms_cyclone`, `terms_arken`, `license_offer`, `payment_data_consent` (apps).

HTML page: `GET /v1/legal-documents/{id}` (`public_url` in API response).

## Screen matrix

### Registration / sign-in (app) — 2 required + 1 optional

| Checkbox | Required | `doc_kind` |
|----------|----------|------------|
| I accept the User Agreement | yes | one of `mobile_apps_terms`, `terms_cyclone`, `terms_arken` |
| I consent to personal data processing | yes | `personal_data_consent` |
| I want news and special offers from RaidBoss Studio | no | `marketing_consent` |

Do **not** pre-check optional marketing.

`POST .../users/register` requires `consents`. Missing required → `400 consents_required`.

### Document versions (important)

| Situation | What to call | Result |
|-----------|--------------|--------|
| Auth screen with checkboxes | `registerUser` + **`explicit_consent: true`** | Current Platform versions are stored |
| Silent login without UI | omit `explicit_consent` | Versions are **not** upgraded |
| Docs updated in admin | `getMyConsents` → modal → **`acceptConsents`** | New versions after explicit checkboxes |

Take `id` / `version` only from `getLegalDocument` / `pending_reconsent` — never hardcode. Send `version` as a **string**.

`needs_reconsent` / `pending_reconsent` cover registration-required kinds only (agreement + personal data), not paywall offer.

### Paywall / purchase

| Checkbox | Required | `doc_kind` |
|----------|----------|------------|
| I accept the License Offer | yes | `license_offer` |
| I consent to processing data required for payment | no | `payment_data_consent` |

`POST /v1/payments` requires `consents` with **`license_offer`**. Separate `payment_data_consent` is optional (usually covered by the offer).

### Site forms (contact / newsletter)

Required: `personal_data_consent`. Link `privacy_policy`. Optional marketing: `marketing_consent`.

`POST /v1/sites/{code}/leads`.

## Client integration checklist

1. Before checkboxes: `getLegalDocument(...)` for current `id`, `version`, document URL.
2. After Auth on the checkbox screen: `registerUser({ explicit_consent: true, consents })` (Flutter: `explicitConsent: true`).
3. After login / app start: `getMyConsents()`; if `needs_reconsent` — modal → `acceptConsents`.
4. Never send `accepted: true` / `explicit_consent: true` without showing checkboxes.
5. Surface API `error` / `message` to the user on save failure.

## SDK (TypeScript)

```ts
import {
  createRaidBossClient,
  CONSENT_CHECKBOX_LABELS,
} from "@raidboss/platform-sdk";

const terms = await platform.getLegalDocument("mobile_apps_terms");
const pd = await platform.getLegalDocument("personal_data_consent");

await platform.registerUser({
  platform: "android",
  explicit_consent: true, // checkbox UI; omit for silent sync (no version upgrade)
  consents: [
    {
      document_id: terms.data.id,
      version: String(terms.data.version),
      checkbox_text: CONSENT_CHECKBOX_LABELS.user_agreement,
      accepted: true,
    },
    {
      document_id: pd.data.id,
      version: String(pd.data.version),
      checkbox_text: CONSENT_CHECKBOX_LABELS.personal_data,
      accepted: true,
    },
  ],
});

const mine = await platform.getMyConsents();
if (mine.data.needs_reconsent) {
  await platform.acceptConsents({
    consents: mine.data.pending_reconsent.map((p) => ({
      document_id: p.document_id,
      version: String(p.published_version),
      checkbox_text: /* checkbox label on screen */,
      accepted: true,
    })),
    source_screen: "reconsent_modal",
  });
}
```

## SDK (Flutter)

```dart
final terms = await platform.getLegalDocument('mobile_apps_terms');
final pd = await platform.getLegalDocument('personal_data_consent');

await platform.registerUser(
  platform: 'android',
  explicitConsent: true,
  consents: [
    {
      'document_id': terms['id'].toString(),
      'version': terms['version'].toString(),
      'checkbox_text': 'I accept the User Agreement',
      'accepted': true,
    },
    {
      'document_id': pd['id'].toString(),
      'version': pd['version'].toString(),
      'checkbox_text': 'I consent to personal data processing',
      'accepted': true,
    },
  ],
);

final mine = await platform.getMyConsents();
if (mine['needs_reconsent'] == true) {
  await platform.acceptConsents(
    sourceScreen: 'reconsent_modal',
    consents: [/* document_id, version.toString(), checkbox_text, accepted */],
  );
}
```

Methods: `listLegalDocuments`, `getLegalDocument`, `getMyConsents`, `acceptConsents`, `listSiteLegalDocuments`, `getSiteLegalDocument`, `submitSiteLead`.

HTTP:

```http
POST /v1/applications/YOUR_APP_CODE/users/register
Authorization: Bearer <jwt>
{ "explicit_consent": true, "consents": [ ... ] }

GET  /v1/applications/YOUR_APP_CODE/users/me/consents
POST /v1/applications/YOUR_APP_CODE/users/me/consents
```

## Consent journal

Each acceptance stores: user/email, app or site, `doc_kind`, version, text `content_hash`, checkbox label, IP, UA, platform, source (`registration` / `paywall` / `site_form` / `newsletter` / `other` with `metadata.flow=reconsent` for the modal), timestamp.

Admin: user profile → **Consents** tab; sidebar **Mailing** — active `marketing_consent` + CSV export.

## Notes

- Server computes `content_hash` from published body.
- Client-sent `version` must match the published document (string).
- Version bump: old acceptances stay in the ledger; the new version is written only with checkboxes (`explicit_consent` or `acceptConsents`), not via silent login.
