Muin is in private beta.Watch the public release announcement —talk to us.
Falaah Falaah AI

Data Privacy

Fulfill GDPR and CCPA data-subject access requests within the 30-day window, erase personal data across modules, and track processing activities with ROPA.

The Data Privacy module handles your organization’s statutory obligations under GDPR, CCPA, and adjacent privacy regimes. Three primary surfaces:

  • DSAR management — receive, verify, fulfill, and deliver data-subject access requests within the 30-day GDPR window
  • Records of Processing Activities (ROPA) — document processing purposes, legal bases, data categories, and retention with annual review reminders
  • Consent — configure and deploy the public-facing consent banner tied to the tenant’s GDPR/CCPA posture

All three are at /compliance/privacy. DSAR-related endpoints are rate-limited both per-user and per-tenant to prevent abuse.

The 30-Day DSAR Lifecycle

Under GDPR Article 12(3), you have one month from receipt to respond to a data subject’s access, erasure, or portability request. Muin computes this deadline as received_date + relativedelta(months=1) — a calendar month, so a Feb 1 request is due Mar 1 (not 30 days later). This matches the statutory clock, not a fixed-day count.

The Status Machine (R9-DSAR-TRANSITIONS)

DSARs move through a strict state machine. Attempting an invalid transition returns 409 InvalidStateError with the set of valid next states in the error body.

received
  ├→ identity_verification
  │    ├→ in_progress
  │    └→ rejected
  ├→ in_progress
  │    ├→ pending_approval ──→ completed
  │    ├→ extended ──→ in_progress (one 60-day extension allowed)
  │    ├→ completed
  │    └→ rejected
  └→ rejected (terminal)

Key rules enforced by the state machine:

  • You can’t skip from receivedcompleted without either verifying identity or acknowledging explicit risk
  • Once in pending_approval, the only way forward is completed (after compliance owner approval) or back to in_progress
  • extended is entered with an explicit reason + new due date (GDPR allows one two-month extension for complex requests)
  • rejected is terminal — rejection requires a documented justification that the subject sees

The full transition matrix is in muin_core/models/compliance/privacy.py (DataSubjectRequest.VALID_TRANSITIONS).


Filing a DSAR

On /compliance/privacy, switch to the DSARs tab and click New Request. The form asks for:

FieldPurpose
Request typeaccess, erasure, rectification, portability, objection, restriction
Subject nameSubject’s full name
Subject emailSubject’s email (used for all communications)
Request detailsFree text — what the subject is asking for
Proof of identityOptional file upload at intake

Subject Picker vs Free-Text Email

For subjects who are known entities in your tenant (employees, customers with accounts, CRM contacts), use the Subject Picker at the top of the form to resolve the request to an existing person record (User, Employee, or Contact). The form pre-fills name and email from that record.

For subjects who are not entities in your tenant (anonymous site visitors, platform data subjects from third-party data flows), skip the picker and enter name + email directly.

The picker matters because picking a person lets the cross-module erasure (below) find all their records automatically — it knows the person_id to search for. For free-text submissions, erasure searches by email address, which is correct for most flows but misses records that don’t store email.

The Received Date

The received_date is set to the form’s timestamp by default. For requests that came in via email before you filed them in Muin, override to the actual receipt date — the 30-day clock should start when you got the request, not when you logged it.


Fulfilling a DSAR

Identity Verification

Click Start Verification on a received DSAR. The verification screen lets you:

  • Upload proof-of-identity documents
  • Record verification method (government ID, tenant-known-account, phone callback, etc.)
  • Add a verification note for audit

On save, the DSAR transitions to in_progress. If the subject can’t verify, transition to rejected with the rejection reason — the subject sees the rejection via the tenant’s configured response channel.

Data Discovery (Cross-Module Erasure)

For erasure requests, Muin’s GDPRDeletionService.erase_for_subject searches across every module for records referencing the subject:

  • HR (employee records, payroll, benefits)
  • CRM (contacts, deals, activities)
  • Finance (customer records, invoices, payments)
  • Documents (uploaded files tagged with the subject)
  • Support (tickets, conversations)
  • Marketing (email subscribers, campaign engagement)
  • Compliance itself (prior DSARs, consent records, incidents involving the subject)

Each module’s records are either deleted outright or pseudonymized (references replaced with hash-stable opaque IDs) depending on what’s legally required to retain for other purposes (tax, dispute windows, ongoing litigation holds).

The service returns a structured summary of what was erased, what was retained (with legal justification), and any records that couldn’t be processed automatically (flagged for manual review). That summary becomes part of the DSAR’s delivery artifact.

Approval and Completion

Once the data package is assembled (for access/portability) or the erasure is executed (for erasure), transition the DSAR to pending_approval. The compliance owner reviews the response before it goes out. On approval, transition to completed and use the Complete dialog to record:

  • Response method — email, secure portal link, physical mail
  • Response document — the data package (optional UUID picker)
  • Response details — free-text summary sent to the subject

The completion is logged with user, timestamp, and response method for audit.

Extension

For complex requests, GDPR allows extending the deadline by two additional months. On an in_progress DSAR, click Extend. The form requires:

  • New due date — must be at most received_date + 3 months
  • Extension reason — free text, sent to the subject

The subject is notified of the extension within the original 30-day window (also GDPR-required). The DSAR transitions to extended, and the dashboard countdown resets to the new deadline.


Rate Limits (D10)

DSAR endpoints are rate-limited using enforce_multi_rate_limit with both per-user and per-tenant buckets on the same request:

EndpointRate-limit key (endpoint name in config)
List DSARscompliance.dsar_list
Lookup a single DSARcompliance.dsar_lookup
Create DSARcompliance.dsar_create
Update DSARcompliance.dsar_update
Complete DSARcompliance.dsar_complete

The per-user bucket prevents a single employee from hammering the API. The per-tenant bucket prevents a script-driven spike across many user tokens from degrading the endpoint for everyone. Exceeding either returns 429 Too Many Requests with a structured dsar_rate_limit_exceeded log entry and a Retry-After header.

The actual limit values are tenant-configurable via the PlatformConfigCache — so a tenant with genuinely high DSAR volume (large consumer brand with many access requests per day) can raise their limit without a code deploy. Defaults are tuned for a typical B2B SaaS tenant.


Records of Processing Activities (ROPA)

GDPR Article 30 requires documenting each processing activity. The Processing Activities tab on /compliance/privacy is the tenant’s ROPA register.

Creating an Activity

Click New Processing Activity. Fill in:

FieldPurpose
Namee.g. “Customer billing”
PurposeWhy you process this data
Legal basisconsent, contract, legal_obligation, vital_interests, public_task, legitimate_interests
Data categoriesWhat kinds of data (names, emails, payment info, health data, …)
Data subjectsWhose data (customers, employees, vendors, minors, …)
RecipientsWho you share the data with
Retention periodHow long you keep it
Transfers outside EEACountries + transfer mechanism (SCCs, adequacy, BCRs)
Security measuresControls protecting this data
Next review dateAuto-set to 1 year out

The Staleness Notifier (R9-ROPA-WORKER)

A daily worker (compliance_ropa_staleness_task) scans all active processing activities. For any activity where next_review_date is within 14 days (or past), it:

  1. Fires a notification to the compliance owner
  2. Creates a HumanTask(category=ROPA_REVIEW) pointing back to the activity

This keeps the ROPA from decaying — a yearly review on an active register means you never show the auditor a 3-year-old processing description.

Updating a Review

Clicking Mark Reviewed on an activity resets next_review_date = today + 1 year (calendar year via relativedelta). Any fields changed during the review are versioned for audit.


The tenant’s public-facing consent banner is configured via the Consent tab. Configure:

  • Which jurisdictions the banner applies to (GDPR, CCPA, UK-GDPR, …)
  • The categories of cookies / trackers the tenant uses (analytics, advertising, functional, strictly-necessary)
  • The cookie policy URL
  • Banner copy in the tenant’s supported languages

The ConsentBanner component (rendered at the root of user-facing apps) reads this configuration and shows the banner only to visitors whose inferred jurisdiction requires consent. Consent decisions are recorded in consent_records with subject identifier, scope, granted categories, timestamp, IP, and user agent.

The banner itself is driven by the ConsentBanner React component in muin.ui/src/components/features/compliance/. Each tenant can enable or disable it per environment.


FAQs

What’s the DSAR deadline?

GDPR Article 12(3) requires response within one month of receipt — a calendar month, not 30 days. Muin computes the deadline as received_date + relativedelta(months=1), so a Feb 1 request is due Mar 1 (not Mar 3). For complex requests, GDPR allows one two-month extension; Muin enforces new_due_date <= received + 3 months when extending, and requires notifying the subject of the extension within the original month.

How does erasure work across modules?

The GDPRDeletionService.erase_for_subject searches every module for references to the data subject — HR, CRM, Finance, Documents, Support, Marketing, and Compliance itself. Records are either deleted or pseudonymized (opaque hash-stable IDs replace references), depending on legal retention requirements. The returned summary documents what was erased, what was retained with legal justification, and what needs manual review.

Why was I rate-limited?

DSAR endpoints enforce two rate-limit buckets at once: per-user and per-tenant. Either bucket can trigger a 429. The per-user bucket prevents a single employee from script-hammering the API; the per-tenant bucket protects the endpoint for everyone when a token leak or misbehaving integration spikes traffic. The Retry-After header in the 429 tells you how long to wait; the log emits dsar_rate_limit_exceeded with your user ID, tenant ID, endpoint, and the bucket that denied the request so admins can debug.

Can I delete a DSAR record?

You can soft-delete a DSAR (archives it, removes it from active views), but the audit record — including all status transitions, response method, and completion timestamp — is retained for the legally required period. The archive is for removing administrative clutter from the dashboard, not for erasing the audit trail.

What if the subject can’t be found in any module?

That’s a normal outcome — the data subject may simply not be represented anywhere (e.g. they contacted a marketing email that wasn’t stored, or their account was already purged for unrelated reasons). The response should document “no personal data found for this subject” and provide that statement to the subject. This is a valid GDPR response and Muin records it as a completed DSAR with “no-data” response_method.