Legal Documents (TOS, Privacy, Waivers)
Linear epic: Legal compliance (TOS/Privacy/Cookie/Waiver acceptance) Status: Shipped and in production. Last reviewed: 2026-05-28
What
Platform-level legal documents (Terms of Use, Privacy Policy, Cookie Policy, Acceptable Use, Fitness Waiver) and the per-user consent ledger that records which version each user accepted, when, from what IP, with which user-agent, and in what context (signup, reconsent, profile edit).
Distinct from compliance forms (see ../forms/):
| Aspect | Legal documents | Compliance forms |
|---|---|---|
| Owner | FitKit platform | Each org / gym |
| Audience | Every user | Org members |
| Storage | URL pointer + consent ledger | PDF in R2 + signature artefact |
| Locale | per-locale rows | per-template locale field |
| Output | A legal_consents audit row | A signed PDF + audit row |
| Israeli regulator surface | Privacy / consumer law | Sports Promotion Law |
Why
- Israeli Privacy Protection Law + GDPR-equivalent require recorded, version-tied consent.
- Reconsent when documents change (the user MUST re-accept a new TOS version before continued use).
- The fitness waiver is a per-user platform-level waiver complementing the per-gym
health_declarationcompliance form.
Personas
| Persona | Surface | Capabilities |
|---|---|---|
| Any user | sign-up flow + legal-acceptance-form.tsx | Read documents, check boxes, accept; recorded with IP/UA/context |
| Returning user with outdated consent | Reconsent prompt at app entry | Accept the new version |
| Org owner during onboarding | Initial signup wizard | Accept TOS + privacy + waiver |
| All users | Profile / settings | View consent status (per type), see version they accepted, dates |
Capabilities
- Document catalog (
legal_documents) — versioned by(type, version, locale)unique key.published_atorders versions. URL field points to the marketing-site canonical (https://fitkit.fit/{slug}). - Consent ledger (
legal_consents) — append-only per user × document. Soft-revocable viarevoked_at(no UI today). Recordsip_address,user_agent,consent_context,organization_id(audit trail). - Current-version resolver —
LegalService.getLatestDocuments(locale)returns one row per type, latestpublished_at(deterministic tiebreak on id). - Consent status —
getConsentStatus(userId)returns per-type{ requiresReconsent, isCurrentVersion, consentedAt }. - Gates —
hasRequiredConsents(userId)blocks app access until TOS + privacy + fitness waiver are accepted. - Idempotent record —
recordConsentskips documents the user already has an active consent for.
Capabilities (gaps)
| Gap | Tracking |
|---|---|
| Revoke consent UI (the column exists; no API surface) | not tracked |
| Cookie consent banner (cookie_policy type exists; banner not implemented) | not tracked |
| Region-aware document selection (always picks latest globally) | not tracked |
| Audit log integration once FIT-20 lands | FIT-20 |
Related features
- Users / auth —
docs/features/users-auth/. Consent gating happens after Clerk sign-in. - Onboarding —
docs/features/onboarding/. The acceptance form is embedded in the signup wizard. - Compliance forms —
../forms/. Different domain; do not confuse the two during QA. - Marketing site — hosts the canonical document URLs (
fitkit.fit/{slug}).
Source code anchor
- API:
apps/api/src/legal/legal.{service,controller,module}.ts+dto/record-consent.dto.ts - DB schema:
libs/db/src/lib/schema/legal.ts - Shared:
libs/shared/src/lib/schemas/legal.schema.ts - Web component:
apps/web/src/components/legal/legal-acceptance-form.tsx - Marketing fetch helper:
apps/web/src/app/api/legal-document/route.ts
See behavior.md for state and qa-plan.md for tests.