Compliance — Audit, Retention, GDPR
Mittr ships three compliance primitives that together let you evidence “who did what, when” for an auditor and respond to a specific end-user’s “show me / delete me” request:
- Resource audit log — every state-mutating action (endpoint created, alert paused, API key revoked) on the workspace.
- Auth audit log — every authentication-adjacent event (logins, MFA, SAML / OIDC / SCIM, session lifecycle, GDPR job submission).
- Data-subject jobs — per-email export and erasure that scan every table referencing the address.
All three respect per-tenant retention windows so storage doesn’t grow unbounded.
Audit logs
Section titled “Audit logs”Two distinct tables, two distinct shapes:
| Resource audit | Auth audit | |
|---|---|---|
| Backing table | audit_logs | auth_audit_logs |
| Records | Resource state changes | Authentication events |
| Examples | endpoint.create, alert_rule.disable, api_key.revoke | password.login.fail, mfa.verify.success, sso.login.jit |
| User ID | Always present (the actor) | Nullable (failed logins lack a user) |
| Subject field | Resource ID | IdP subject claim or email |
| Read API | GET /api/v1/audit-logs | GET /api/v1/auth-audit (admin-only) |
| Dashboard | Analytics → Activity | Analytics → Auth events |
Both are tenant-scoped — you can never see another workspace’s rows.
Filter pills on each tab let you narrow by resource family or auth
event family (e.g. password. matches both .success and .fail).
Retention windows
Section titled “Retention windows”Each workspace has four independent retention windows, all in days:
| Column | What it caps | Default |
|---|---|---|
event_retention_days | Delivered + dead events | inherit plan default |
delivery_attempt_retention_days | Per-attempt rows for those events | inherit plan default |
audit_log_retention_days | Resource audit rows | inherit plan default |
auth_audit_retention_days | Auth audit rows | inherit plan default |
NULL means “fall back to the plan default.” Setting 0 means
“keep forever, never purge” — useful for the high-compliance auth
trail in regulated environments.
The retention processor runs every hour and applies each tenant’s window per row, so changing a column takes effect on the next cleanup tick (no rebalancing needed).
Set the windows from Settings → Data & Privacy → “Retention” or via the API:
curl -X PATCH https://app.mittr.io/api/v1/me/retention \ -H "Cookie: mittr_session=..." \ -H "Content-Type: application/json" \ -d '{ "eventDays": 90, "deliveryAttemptDays": 30, "auditLogDays": 365, "authAuditDays": 730 }'Negative values are rejected; values above 7300 (≈20 years) are rejected as a storage DoS guard.
Data-subject requests (GDPR)
Section titled “Data-subject requests (GDPR)”Two flavours, both per-email and admin-only:
Export
Section titled “Export”Returns every row in the workspace whose payload, audit metadata, or actor field references the given email. Useful for fulfilling “show me my data” requests under GDPR Article 15.
Submit from Settings → Data & Privacy → “Data subject requests” (enter email, click Export) or via the API:
curl -X POST https://app.mittr.io/api/v1/gdpr/data-export \ -H "Cookie: mittr_session=..." \ -H "Content-Type: application/json" \# → 202 Accepted, returns {id, kind, subjectEmail, status: "queued"}The job runs asynchronously (typically completes in seconds for
normal data sizes). Poll GET /api/v1/gdpr/jobs/{id} for status;
when status: "completed", the dashboard’s Download button
links to GET /api/v1/gdpr/jobs/{id}/download. Export blobs are
retained for 30 days, then auto-purged.
The export schema is:
{ "schema_version": 1, "client_id": "...", "exported_at": "2026-04-26T10:00:00Z", "events": [...], "delivery_attempts": [...], "audit_logs": [...]}Erasure
Section titled “Erasure”Redacts every event payload referencing the email and deletes matching audit rows. Idempotent — re-running after rows are clean just updates zero rows. Useful for “right to be forgotten” requests under GDPR Article 17.
curl -X POST https://app.mittr.io/api/v1/gdpr/data-erasure \ -H "Cookie: mittr_session=..." \ -H "Content-Type: application/json" \What gets touched:
events.payloadfor matching events → replaced with{"_redacted": true, "reason": "gdpr_erasure"}(preserves the delivery row so usage / billing stays consistent).delivery_attempts.response_bodyfor those events → replaced with[redacted: gdpr_erasure].audit_logsrows whereactor_id = emailor metadata mentions the email → deleted.
What doesn’t:
- Auth audit rows. Authentication events are tenant-scoped
security records, not personal data in the GDPR sense — keep
them per your
auth_audit_retention_dayssetting. - The user’s
usersrow, if they’re a workspace member. Use Team → Remove member for that.
Audit trail
Section titled “Audit trail”Every data-subject job emits two auth_audit_logs rows: one at
queue time (gdpr.export.queued or gdpr.erasure.queued) and one
when the runner finishes (gdpr.{export,erasure}.{completed,failed}).
The metadata.job_id field on each row joins them so reviewers
can trace request → outcome.
Plan limits
Section titled “Plan limits”Retention defaults (i.e. what NULL resolves to) come from the
workspace’s plan. Feature gates may also restrict who can access
auth audit logs, GDPR endpoints, and the per-tenant retention
columns; check Settings → Plans for your workspace’s tier.
Operator reference
Section titled “Operator reference”| Endpoint | Method | Auth |
|---|---|---|
/api/v1/me/retention | GET | Session, any role |
/api/v1/me/retention | PATCH | Session, admin only |
/api/v1/auth-audit | GET | Session, admin only |
/api/v1/audit-logs | GET | Session or API key |
/api/v1/gdpr/data-export | POST | Session, admin only |
/api/v1/gdpr/data-erasure | POST | Session, admin only |
/api/v1/gdpr/jobs | GET | Session, admin only |
/api/v1/gdpr/jobs/{id} | GET | Session, admin only |
/api/v1/gdpr/jobs/{id}/download | GET | Session, admin only |