Compliance is a first-class concern for a SaaS boilerplate. This page covers the built-in privacy mechanics for GDPR-style data-subject rights plus the launch checks needed for US, Canadian, and Swiss markets. This is an implementation baseline, not legal advice; review the final policy text and processor list with counsel before production launch.

RightHow it's served
ConsentCookie banner with necessary/analytics/marketing categories; anonymous choices persist locally, while logged-in choices sync via POST /api/user/consents. Third-party analytics/chat scripts mount only after matching consent.
Demonstrable consent (Art. 7(1))user_consents holds the current decision per category; user_consent_events is an append-only trail of every grant and withdrawal. Both stamp policy_version from complianceConfig.privacyPolicyVersion, so a stored consent can be traced to the notice the user actually saw — consent given against a superseded notice is not consent to the current one. The trail is readable by its subject but writable only by the service role: it is simultaneously their personal data and your evidence, and an audit log the subject can write to is not evidence.
WithdrawalThe footer exposes Cookie settings and Your Privacy Choices so users can reopen preferences, withdraw analytics/marketing consent, and reach privacy-rights actions after the banner is dismissed.
Access / portability (Art. 15/20)GET /api/user/export-data streams a multi-section RFC 4180 CSV — 29 sections covering profile, personal accounts, memberships, chat/AI, consents and consent history, payments, subscriptions, licenses, invitations, access/admin logs tied to the user, deletion requests, notification preferences and delivery log, pending emails, document metadata, referral codes and referrals, affiliate applications/attributions/conversions, and API-key metadata without secrets/hashes. It is scoped to the caller's own data only. When you add a table holding personal data, add its section in the same change — and note that tables keyed on a domain id rather than an account id (e.g. affiliate_conversions.affiliate_id) need a second pass, since the account-scoped query alone only catches rows where the subject was the referred party. The Art. 17 erasure cascade is the checklist: any table it deletes is personal data the export must also cover.
Erasure (Art. 17)Self-service deletion request enters a 30-day grace queue (account_deletion_requests). The process-account-deletions job cancels Stripe subscriptions, removes the newsletter contact, cascades licenses to chat to payments to subscriptions to memberships to account, deletes Storage objects (not just rows), then the auth.users row.
B2B owner cascadeaccount_deletion_requests.cascade_members distinguishes "delete my personal account in B2B mode" (cascades workspace members' auth rows) from workspace-only deletion (members preserved). Set by the requestAccountDeletion server action; never elsewhere.
US state privacyThe footer links to /[locale]/privacy-choices, a dedicated "Your Privacy Choices" page for do-not-sell/share and targeted-advertising opt-out visibility. Cookie consent also honors browser Global Privacy Control signals by keeping marketing/sharing/targeted-ad consent disabled even when "Accept all" is clicked.
CanadaThe default policy covers meaningful consent, reasonable purposes, safeguarding, consent withdrawal, breach-response procedures, and CASL-style commercial-message opt-in/unsubscribe expectations.
SwitzerlandThe default policy covers Swiss FADP/nFADP-style transparency, proportionality, purpose limitation, security, data-subject rights, international-transfer safeguards, and FDPIC high-risk breach notification expectations.
Marketing emailPOST /api/newsletter/subscribe is Turnstile-protected and records server-side consent provenance attributes (CONSENT_SOURCE, CONSENT_TEXT, CONSENT_AT) with the configured email provider.

Deletion requests are proof-bearing: processor_status, processor_errors, error_message, and retry_count preserve redacted outcomes for Stripe, newsletter/email, Storage, database cascade, and Supabase Auth. A failed processor marks the request failed instead of falsely completing it.

Customer-facing routes: /[locale]/privacy-choices, /[locale]/privacy, /[locale]/ai-transparency, /[locale]/terms, and /[locale]/legal. API routes: GET /api/user/export-data, POST /api/user/consents, POST /api/org/schedule-deletion, POST /api/org/cancel-deletion (all authenticated; deletion is owner-only at the API). Before launch, edit the terms / privacy / legal CMS pages and confirm your processor list (Stripe, Supabase, email provider, GTM, AI providers).

EU AI Act applies on top of this

If you ship the AI features, Article 50 of Regulation (EU) 2024/1689 binds you independently of GDPR. It has applied since 2 August 2026 and was excluded from the Digital Omnibus deferral that moved high-risk obligations to December 2027. The boilerplate implements the chat disclosure, machine-readable output marking, and the public transparency notice — but the risk classification and the provider DPAs are yours. See EU AI Act compliance.

Four operator deliverables that no boilerplate can generate for you ship as fill-in templates: records of processing (Art. 30), a DPIA (Art. 35, likely required because of the AI features), a 72-hour breach runbook (Art. 33/34), and the subprocessor register and DPA clauses (Art. 28) your B2B customers will ask for.

US/Canada/Switzerland launch checklist: publish real business contact details; verify commercial email templates identify the sender and include unsubscribe where the message is promotional; confirm whether your analytics/ads setup is sale, sharing, or targeted advertising and that /privacy-choices accurately describes it; document breach notification ownership; and create Stripe CAD/CHF prices if Canadian or Swiss checkout is enabled.

Also set LEGAL_COMPANY_NAME, LEGAL_ADDRESS, and LEGAL_REGISTRATION_NUMBER to real production values before enabling NEXT_PUBLIC_INDEXABLE=true. Example placeholders intentionally block public/indexable production builds.