Dashboard Get started

Every organization has independent retention windows for raw event data and for the audit log, plus two erasure endpoints for GDPR right-to-be-forgotten requests. Every purge — scheduled or on-demand — writes an immutable row to the deletion registry, so you always have proof-of-purge for GDPR, HIPAA, or SOC 2 evidence requests.

Configure retention

GET/api/v1/org/{org_id}/retention
{
  "org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "events_retention_days": 365,
  "audit_log_retention_days": 2555,
  "updated_at": "2026-05-21T12:00:00+00:00"
}

Fields

Field Range Default Notes
events_retention_days 1–3650 365 Raw event ledger window
audit_log_retention_days 1–3650 2555 ~7 years — common SOX floor

Update example:

curl -X PUT https://token.audit.id/api/v1/org/{org_id}/retention \
  -H "Authorization: Bearer td_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"events_retention_days": 90}'

A nightly trim job deletes rows older than each cutoff, scoped per-org. The dashboard surfaces a Last purge indicator on Settings → Data Retention so you can confirm the job is healthy.


Deletion registry

Every purge — nightly trim, subject erasure, or full org erasure — writes a row to the immutable deletion_registry table capturing:

Column Description
org_id The org whose data was purged
actor_id The user who triggered the deletion (null for the nightly job)
reason nightly_retention, gdpr_subject_erasure, or org_data_erasure
counts {events, usage_records, audit_log, digests_invalidated, …}
notes Optional free-text context (e.g. subject_id=…)
created_at When the deletion ran

Registry rows are append-only and survive even the full-org erasure that produced them. They are the canonical evidence trail for GDPR / HIPAA / SOC 2 audits.


Subject erasure (GDPR right to be forgotten)

DELETE/api/v1/org/{org_id}/subject/{subject_id}/events

Erases every event whose payload.subject_id matches. Requires the ORG_DATA.DELETE permission. Pass dry_run=true to count matches without deleting — recommended for any ticket-driven workflow.

# preview
curl -X DELETE "https://token.audit.id/api/v1/org/{org_id}/subject/user_42/events?dry_run=true" \
  -H "Authorization: Bearer td_live_xxxx"

# execute
curl -X DELETE "https://token.audit.id/api/v1/org/{org_id}/subject/user_42/events" \
  -H "Authorization: Bearer td_live_xxxx"
{
  "dry_run": false,
  "subject_id": "user_42",
  "events_found": 184,
  "events_deleted": 184,
  "digests_invalidated": 3
}

The endpoint also marks any Merkle digest whose window overlapped the erased events as invalidated_at with reason gdpr_subject_erasure. The original digest rows remain in the table — they are flagged, not deleted — so the audit trail of what was once provable is preserved even after the underlying events are gone. See Event signatures for what an invalidated digest means for batch verification.


Full org erasure

DELETE/api/v1/org/{org_id}/data

Cascading erasure of the entire organization — events, usage records, audit log, retention config, the org row itself. Requires the owner role plus the ORG_DATA.DELETE permission. The caller must echo the org slug or id in confirm_org; a mismatch returns 400 before any delete happens.

curl -X DELETE https://token.audit.id/api/v1/org/{org_id}/data \
  -H "Authorization: Bearer td_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "confirm_org": "acme-corp",
    "notes": "Account closure ticket #4218"
  }'
{
  "ok": true,
  "deleted": {
    "events": 184290,
    "usage_records": 184290,
    "audit_log": 8421,
    "organizations": 1
  }
}
This is irreversible. Every org-scoped row is removed; only the `deletion_registry` row survives. There is no undo and no soft-delete window. The `confirm_org` guard exists specifically to prevent path-typo accidents.

Aggregated summaries

Daily and monthly cost / token summaries are kept indefinitely regardless of events_retention_days. Only raw per-event payloads age out — dashboards and management reports continue to render history far beyond the event ledger's cutoff. The append-only BigQuery replica honors the same window via a BQ-side table expiration policy; no separate configuration needed.