Enterprise
Audit log
What is recorded, how to filter it, and how to export it as CSV or JSON.
Every workspace has an append-only audit log: who did what, when, and from where. Owners and admins read it in Settings → Audit log, export it as CSV or JSON, and agents or scripts can pull it with an API key.
Who can see it
- Roles: owners and admins. API keys need the
audit:readscope. - Self-hosted: the licence's
audit_exportfeature grants the whole log (Team and Enterprise editions). Without it, the log is not available. - critlab cloud: Enterprise sees the whole log, Team the last 7 days.
These rules limit what can be read, not what is kept; how long events are kept is under Retention.
What is recorded
Each event has a time, an action, the actor (a person, an API key, or the SCIM directory), the subject (a member, prototype, folder…), the request's IP address and user agent, and action-specific details.
| Area | Actions |
|---|---|
| Sign-in | auth.login, sso.login |
| Members and invitations | member.invited, member.invitation_revoked, member.joined, member.role_changed, member.removed, member.folder_granted, member.folder_revoked, member.folder_context_changed |
| Access requests | access.requested, access.approved, access.denied |
| Prototypes and versions | target.created, target.access_changed, target.moved, target.status_changed, target.link_comments_changed, target.require_name_changed, target.external_assets_changed, target.site_changed, target.snippet_key_rotated, target.deleted, version.deleted |
| Folders | folder.created, folder.renamed, folder.moved, folder.reordered, folder.styled, folder.image_changed, folder.archived, folder.unarchived, folder.deleted, folder.context_added, folder.context_updated, folder.context_removed |
| Comments | comment.deleted |
| Single sign-on | sso.updated, sso.enabled, sso.disabled, sso.enforcement_changed, sso.provisioned, sso.role_synced, sso.role_sync_skipped, sso.domain_added, sso.domain_verified, sso.domain_failed, sso.domain_removed |
| Directory sync | scim.user_*, scim.group_*, scim.role_synced, scim.role_sync_skipped, scim.token_created, scim.token_revoked |
| Storage | storage.configured, storage.verified, storage.verification_failed, storage.disconnected |
| Licence and keys | license.updated, apikey.created, apikey.revoked |
| Operator actions (critlab cloud staff) | admin.* — recorded in the affected workspace's own log |
Tokens, passwords and licence keys are never written to the log.
Filter
In the settings page, or as query parameters on GET /api/workspaces/{id}/audit:
| Filter | Matches |
|---|---|
actor | A person, by user id or email |
folder | A folder by id or path (e.g. Acme/Website), its subfolders and every prototype filed in them |
prototype | A prototype by id or slug; a deleted one by id |
action | One action, e.g. member.role_changed |
since, until | A time window (ISO 8601) |
Filters combine. Pages are cursor-based, so reading while new events arrive never skips or repeats one.
Export
- CSV: the export button in settings, or
?format=csv. - JSON:
?format=json, or from the CLI:
crit audit export --since 2026-09-01T00:00:00Z --folder "Acme/Website" --jsonExports honour every filter.
Retention
Audit events are kept for 13 months, the same for every plan, and then deleted by the retention job, which web runs by itself every hour. Export the log on your own schedule if you need to keep it for longer. A self-hosted install can change the period with RETENTION_AUDIT_MONTHS (0 keeps events forever); see scheduled jobs for how the job runs and how to switch it off.
Operator notes
- The IP address is the client as your deployment's trust model names it: the entry of
X-Forwarded-Forthat many places from the right asTRUSTED_PROXY_HOPSsays (default1, the last entry, the one your own proxy appended; the entries to its left are whatever the client wrote and are never used), elseX-Real-IPwhen there is noX-Forwarded-For. SetTRUSTED_PROXY_HOPSto the number of proxies of yours in front of the app, each of which must append toX-Forwarded-Forrather than replace it (Caddy and ingress-nginx replace by default; see the configuration reference), andTRUST_CF_CONNECTING_IP=1for a Cloudflare-only origin; without a proxy that sets these headers, events carry no IP address. Reading the source code of a prototype records no address at all for someone who is not a member of the workspace. - If writing an event ever fails, the user's action still succeeds and web logs the full event to stderr with
console.error. Alert on those lines if the log is part of your compliance evidence; see Health checks, monitoring and logs.