Every workspace ships with an immutable audit trail at
/app/audit. Admin and Owner roles can browse, filter,
and inspect entries; Editor and Viewer are blocked.
The application writes to audit_logs for any action
that materially changes workspace state or surfaces compliance
exposure. Examples include:
dsr.exported) and erasures
(dsr.erased).
The application writes the following audit actions today. Filter
on the Action toolbar field to surface a specific
slug (substring match — agent shows every
agent.* row).
agent.created / agent.updated /
agent.deleted / agent.bulk_deleted
— workspace owner creates / edits / removes an AI agent.
Bulk-delete fires one row per affected agent so cross-tenant
scoping is visible.agent.published / agent.republished
/ agent.rolled_back — the publish lifecycle.
Republished fires when a draft change is re-published on top
of an existing live version.source.created / source.deleted —
knowledge sources attached to an agent (URL crawls, files,
Notion, Google Docs, etc.).workflow.created / workflow.updated
/ workflow.deleted /
workflow.bulk_deleted — scripted-reaction
workflows.member.invited / member.added /
member.removed — invitation, direct-add (when the
invitee already had a Pitchbar account), and seat removal.api_token.created /
api_token.revoked /
api_token.forgotten /
api_token.purged — workspace API token lifecycle.
Forgotten = hard-deleted (single row). Purged = bulk
hard-delete of every revoked row for the workspace.platform_settings.updated — super-admin saved a
section of /settings/system. The after
column records the section name + a list of changed
non-sensitive keys; secret values (API keys, webhook secrets,
SMTP passwords) are stripped before write so the audit row
cannot leak credentials.integration.webhook_secret_rotated — admin
rotated a webhook delivery secret.dsr.exported / dsr.erased — GDPR
data-subject-request fulfillment.impersonation.started /
impersonation.stopped — super-admin acted on
behalf of a workspace member.
When an action fires from a context with no authenticated user
(queue worker, scheduled command, webhook callback), the row's
after.system_origin field carries a marker:
console, webhook_or_queue, or
background. Use this to distinguish "system did this"
from "we forgot to capture the actor".
Each row carries: action slug, entity type / id, actor user (or
System when triggered by a queue job), originating IP,
user agent, and a before / after JSON
snapshot.
The toolbar accepts:
dsr shows both exported and erased rows.created_at. Either bound is optional.Filters compose; pagination respects the active filter set.
Audit rows are pruned daily via the scheduled command
audit:prune --days=365. Default retention is one
year — long enough to investigate "what changed two quarters
ago?" but bounded so the table doesn't grow without limit.
Customers with stricter compliance requirements (SOC2, HIPAA,
long-tail GDPR holds) should export their audit log via the
artisan command on their own cadence before this prune fires.
Override the window by passing a different --days
value in your routes/console.php.
# Dry-run: report what would be pruned, change nothing.
php artisan audit:prune --days=365 --dry-run
# Force a longer retention window.
php artisan audit:prune --days=730
Audit rows are append-only. There is no UI to edit a row, and
the database has no updated_at column on
audit_logs. This is intentional — a compliance log
that can be rewritten is not a compliance log.
The table is indexed on (workspace_id, created_at)
and (entity_type, entity_id). A workspace with
tens of thousands of rows still serves the first page in under
50ms. If you need to bulk-export, query the table directly via
the workspace:audit-export Artisan command (planned)
rather than scraping the UI.