GDPR Article 30 requires controllers to maintain a written record of processing activities. The exemption for organisations under 250 employees does not apply when processing is regular (yours is — every login is processing), so assume you need this.
This is the register for a stock deployment. It is a starting point you adapt, not a document you file as-is: the controller identity, retention periods, and transfer safeguards are yours to fill in, and any feature you add creates a row that is not here.
Controller
| Field | Value |
|---|---|
| Controller | LEGAL_COMPANY_NAME (see config/app.ts → appConfig.legal) |
| Address | LEGAL_ADDRESS |
| Registration | LEGAL_REGISTRATION_NUMBER |
| DPO | DPO_EMAIL — leave unset if you have not appointed one. Publishing a fake DPO contact is worse than publishing none. |
| Lead supervisory authority | SUPERVISORY_AUTHORITY |
Article 37 makes a DPO mandatory for large-scale regular monitoring or large-scale special-category processing. A typical SaaS is below that line, but AI usage logging pushes some deployments closer than operators expect.
Processing activities
1. Account management and authentication
| Purpose | Create and secure accounts, authenticate users, manage workspace membership |
| Legal basis | Art. 6(1)(b) performance of a contract |
| Data subjects | Registered users, invited workspace members |
| Categories | Email, name, phone, birthday, avatar, auth identifiers, membership role |
| Tables | profiles, accounts, memberships, invitations |
| Recipients | Supabase (hosting + auth), email provider (magic links) |
| Retention | Life of the account, then the 30-day deletion queue |
2. Billing and payments
| Purpose | Process subscriptions, credit packs, licenses; invoicing; tax records |
| Legal basis | Art. 6(1)(b) contract; Art. 6(1)(c) legal obligation for tax retention |
| Categories | Billing identifiers, provider customer/subscription/payment ids, amounts, currency |
| Tables | subscriptions, payments, licenses, credit_transactions |
| Recipients | The configured payment provider: Stripe, Lemon Squeezy, or Paddle |
| Retention | Statutory accounting period (commonly 7–10 years) — survives account deletion; state your local period here |
Card data never reaches the application; the configured hosted payment provider holds it. The selected provider must match the public privacy notice and current subprocessor register.
3. AI features (chat, agents, RAG)
| Purpose | Generate responses to user prompts, index and search uploaded documents |
| Legal basis | Art. 6(1)(b) contract |
| Categories | Prompt text, generated output, uploaded document content and embeddings, token counts, model/agent ids |
| Tables | chat_sessions, chat_messages, ai_requests, documents, document_chunks |
| Recipients | The LLM providers configured in aiConfig.providers — see /ai-transparency |
| Transfers | Provider-dependent; typically EU + US |
| Retention | Life of the account; cleanup-sessions purges old sessions on the configured schedule |
This is the highest-risk activity in the register. Users paste arbitrary content into prompts, including data about third parties and occasionally special-category data, and it leaves your infrastructure. It is the main reason a DPIA is likely required — see DPIA.
4. Consent management
| Purpose | Record and demonstrate cookie/marketing consent |
| Legal basis | Art. 6(1)(c) legal obligation (Art. 7(1) demonstrability) |
| Categories | Consent category, decision, timestamp, policy version, IP, user agent |
| Tables | user_consents (current state), user_consent_events (append-only trail) |
| Retention | Kept beyond withdrawal — the proof is the point |
5. Security, audit and operational logging
| Purpose | Detect abuse, investigate incidents, maintain an admin audit trail |
| Legal basis | Art. 6(1)(f) legitimate interest (securing the service) |
| Categories | Hashed IP, user agent, event type, geolocation, admin actions, redacted error messages |
| Tables | user_access_logs, admin_logs, error_logs |
| Retention | LOGS_RETENTION_DAYS (default 90), enforced by purge-error-logs |
A legitimate-interest basis requires a documented balancing test. Write it down; "we needed it" is not a balancing test.
error_logs uses the following field-level DSAR and erasure policy:
| Fields | Data export | Account/user erasure |
|---|---|---|
id, account_id, level, status_code, created_at | Exported for events attributed to the subject, plus events belonging to the subject's personal Account. Workspace-wide events are never exported to an ordinary member. An export exceeding the automated bound fails explicitly into manual review instead of being silently truncated. | Account attribution is removed; only level, status and timestamp remain until normal retention. |
actor_user_id | Used to scope the export but omitted from the file so a personal-Account event cannot reveal another operator. | Set to NULL. |
category, event, route, method, error_code, message, stack_redacted, ip_hash, metadata | Excluded from the automated export because caller-controlled, free-form or pseudonymous diagnostics can contain third-party or security-sensitive data. Handle a justified broader DSAR through manual review. | Replaced or cleared by the bounded GDPR RPC. A database trigger performs the same scrub atomically if an Auth/Account foreign key loses attribution after the application drain. |
The automated export combines actor-attributed events across Accounts with
personal-Account events, deduplicates them by id, and caps each source query.
The durable deletion processor anonymizes rows before foreign keys can be set
to NULL; an attribution-loss trigger closes the concurrent-write window, and
direct table updates remain unavailable to ordinary runtime and provider-native
client roles.
6. Transactional and marketing email
| Purpose | Magic links, billing notices, invitations; newsletter where opted in |
| Legal basis | Art. 6(1)(b) for transactional; Art. 6(1)(a) consent for marketing |
| Tables | pending_emails, notification_preferences |
| Recipients | Brevo or Mailjet, per EMAIL_PROVIDER |
7. Referral and affiliate programs
| Purpose | Attribute referrals, calculate rewards and commissions, prevent fraud |
| Legal basis | Art. 6(1)(b) contract; Art. 6(1)(f) for fraud prevention |
| Categories | Attribution links, hashed IP and user agent, application text, commission amounts |
| Tables | referral_codes, referrals, affiliate_* |
| Retention | Life of the account plus any commission dispute window |
Only active when REFERRAL_ENABLED / AFFILIATES_ENABLED are on. Remove these rows if you ship with the features off.
8. Push notifications
| Purpose | Deliver credit alerts and system announcements |
| Legal basis | Art. 6(1)(a) consent (browser permission) |
| Tables | push_subscriptions, notification_log |
| Retention | Until unsubscribed; cleanup-push-subscriptions removes stale tokens |
Technical and organisational measures (Art. 32)
Row-level security scoped by Account membership and the verified application actor; separate least-privilege credentials for runtime, workers, maintenance and platform administration; CSRF on state-changing routes; rate limiting; secret redaction and IP hashing in logs; SSRF validation on webhook URLs; constant-time secret comparison; recent-auth gate on sensitive admin actions; and encryption in transit and at rest through the selected managed providers.
Maintenance
Review when you add a table holding personal data, add or change a subprocessor, change a retention period, or change a legal basis. Bump PRIVACY_POLICY_VERSION in config/compliance.ts whenever the change affects what the privacy notice says.
Optional native product insights and AI allowances
Native product measurement stores Account/actor identifiers, milestones and timestamps only after current analytics consent. Withdrawal erases the actor’s measured events and cohort markers. Operational activation progress and AI allowance/accounting records are separate purposes. Events use bounded 90-day retention; first-observation markers prevent cohort resets and remain until withdrawal or erasure. Policy audit and monthly counters use a 365-day retention window; minimal accounting replay keys and unresolved receipts remain until Account erasure. The subject export includes both domains. Update the deployment’s processing register and privacy notice before enabling these purposes; details and configuration are in Product insights and AI controls.