GovernanceAudit log
Audit log
There is not one audit log, there are several, and they answer different questions: what the AI did, what an administrator changed, and what data left the workspace. This page is what each one records, who can read it, and the two things it deliberately does not claim.
What is recorded, and where
- The activity trail
- Agent decisions, grounded AI responses, consent changes, lead assignment and book reassignment, committed contact and opportunity imports, GDPR erasures including the ones that were blocked, compliance sweeps, the SMS send gate, and webhook subscription lifecycle. Each row carries the workspace, the acting user, the action name, the model, tokens, latency, and a structured input and output.
- The configuration trail
- Before and after on the settings that change how the product behaves: the workspace autonomy level, business hours, the recording-consent override, report branding, API keys created and revoked, and the inbound transcript webhook token being rotated.
- The export trail
- One row per export attempt, allowed or refused, naming the export type, the permission checked, the record count and the format.
- The import trail
- One row per imported record rather than a summary: the row number, what it landed on, whether it was accepted, skipped, deduped or failed, and the reasons.
- The model usage ledger
- Every model call with its provider, purpose, token count, latency and whether it succeeded. Useful for cost questions, and it is the record that shows an AI action happened at all.
Be clear about the boundary: this is a trail over named categories, not a journal of every request. Most API routes write no audit row. If a specific action needs to be provably recorded for your own compliance programme, ask us whether it is, rather than assuming the presence of an audit log covers it.
Who can read what
Reading is split deliberately, because an audit row's payload can contain the content of the thing being audited.
- Any workspace member can read the activity trail in summary: the action, the actor, the model, tokens, latency and the timestamp. The model usage ledger is member-readable in the same way.
- The input and output payload on an activity row requires an admin workspace role. Asking for it as a non-admin is a 403 rather than a row with the payload quietly stripped.
- The configuration trail and the control-plane events are admin-only in the workbench. A non-admin is told they are admin-only rather than shown an empty list, so an absence is never mistaken for silence.
Before values in the configuration trail are redacted by default. A setting is only recorded with its prior value where the prior value is itself part of the record, which is why a recording-consent override says what the consent requirement was and an API key change does not restate a secret.
Retrieving it
The audit workbench in the product is the reading surface: the four sources merged into one reverse-chronological list, most recent first, capped at a hundred rows per source.
There is also a session-authenticated JSON endpoint for the activity trail, filterable by action name and by acting user, and paged with an offset and a limit that defaults to fifty and is capped at a hundred. It returns an exact total count and a has_more flag alongside the rows. Asking for the payload is a query flag, and it is the flag the admin check applies to. The row-level import trail is retrieved per import job.
The audit log is not part of the public API. The nine operations on /api/v1 are contacts, deals, sequences and suppression; none of them reads audit rows, and an API key cannot pull the trail. Retrieval is from inside the product, as a signed-in member of the workspace.
Two things this log does not claim
It is not tamper-evident, and we would rather say so than let the phrase audit log imply it. A tamper-evidence endpoint exists and it answers honestly: the rows carry no stored hash links, so it reports that no persisted chain is available and returns a not-implemented status rather than a verification result. Rebuilding a chain from the current rows would only prove the freshly built payload is internally consistent, which is not evidence that a historic row was never edited or removed.
There is also no retention control. The product does not offer a configurable audit retention period, and nothing trims the trail on a schedule. Call recordings do carry a retention setting; the audit log does not, and you should not read a recording retention number as applying to it.
One place the trail is treated as load-bearing rather than best-effort: an export whose audit row cannot be written is refused rather than released. Data that leaves without its record is unrecoverable, and a blocked export can simply be retried.
Behaviour on this page is read from
- src/app/api/audit/route.ts
- src/app/api/audit/verify-chain/route.ts
- src/app/api/import/jobs/[id]/audit/route.ts
- src/lib/audit-trail.ts
- src/lib/audit-log-hash-chain.ts
- src/lib/workspace-configuration-audit.ts
- src/lib/export-audit.ts
- src/lib/crm-import-audit.ts
- src/lib/webhooks/webhook-lifecycle-audit.ts
- src/lib/admin-access.ts
Was this page helpful?