B Blengi docs

Run your workspace

Audit log

Every workspace has an immutable audit trail. It's a super-admin (platform operator) surface — not a customer feature — so it lives on the platform admin, not the customer workspace. Open any workspace under /admin/workspaces and click Audit log (/admin/workspaces/{workspace}/audit) to browse, filter, and inspect its entries. Customers never see it; the route 404s anyone who isn't a super-admin. Audit rows keep being written for every workspace regardless of who can read them.

The entries can contain PII (lead emails, originating IPs), which is why viewing is restricted to the operator. To see exactly what a customer sees in their own interface, impersonate them from the workspace page instead.

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. 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.

Filtering

The toolbar accepts:

  • Action — substring match (case-insensitive), e.g. dsr shows 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.