Run your workspace
Audit log
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.
What gets logged
The application writes to audit_logs for any action
that materially changes workspace state or surfaces compliance
exposure. Examples include:
- Member invitations, role changes, removals.
- Integration connect / disconnect (Slack, Teams, webhooks).
- Integration webhook delivery failures (so broken Slack URLs are visible without tailing logs).
- GDPR data-subject-request exports
(
dsr.exported) and erasures (dsr.erased). - Billing-plan changes initiated from the admin UI.
Action vocabulary
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. Theaftercolumn 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.
Filtering
The toolbar accepts:
- Action โ substring match (case-insensitive),
e.g.
dsrshows both exported and erased rows. - Entity type โ dropdown populated with the distinct entity types currently in your workspace's log, so you only see options that actually have history.
- Actor email โ exact match on the user's email; pass an unknown email to surface only system-initiated rows.
- From / To โ bracket
created_at. Either bound is optional.
Filters compose; pagination respects the active filter set.
Retention
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
Why no edit / delete
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.
Performance
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.