Audit log
Every project-affecting action, who did it, and when - the actual event catalog, not an aspirational one.
What gets recorded
Every state-changing action against a project lands in an append-only audit trail. Each event carries:
- action — a dotted event name from the catalog below
- actor — who did it: a dashboard user, an API key, a platform operator, or the system itself
- metadata — action-specific context (job IDs, backup IDs, labels, ticket references)
- created_at — when
For asynchronous operations, the event is recorded when the action is accepted (the job enqueued), so the trail reflects intent the moment it happens — the job's own success or failure is tracked on the job itself.
Event catalog
These are the events the backend emits today. No others exist.
| Event | Recorded when |
|---|---|
project.deleted | Project deletion enqueued |
project.credentials_rotated | Credential rotation enqueued |
project.restore_overwrite_started | A production-overwrite restore enqueued |
project.import_preflight | Import preflight run against a source |
project.import_upload_created | Import upload slot created |
project.import_started | Import enqueued |
project.integration.connected | Vercel / Netlify / Clerk integration connected |
project.integration.upsert | Integration settings created or updated |
project.integration.delete | Integration disconnected |
preview.ttl_extended | A preview database's TTL pushed out |
restore_point.created / restore_point.deleted | Restore point pinned / removed |
cutover.requested | Cutover request opened |
cutover.acked / cutover.rejected | Operator acknowledged / rejected the request |
cutover.promote_started | Operator started executing the cutover |
production_overwrite_restore | Cutover promotion completed (carries the pre-cutover backup ID) |
api_key.created / api_key.revoked | Organization or project API key minted / revoked |
webhook_endpoint.created / webhook_endpoint.updated / webhook_endpoint.deleted | Webhook endpoint lifecycle |
webhook_endpoint.secret_rotated | Webhook signing secret rotated |
studio.row_inserted / studio.row_updated / studio.row_deleted | Row edited through Studio |
Worth noticing what is not here: reads. Listing projects, viewing connection strings, and running SELECTs do not generate audit events. The trail answers "who changed what", not "who looked at what".
Viewing the trail
- Dashboard — the project overview shows recent activity, rendered with friendly actor and action labels.
- API —
GET /v1/projects/{id}/audit-eventsreturns the full trail, newest first, with alimitparameter. See the API reference.
curl -s -H "X-API-Key: $CAPYDB_API_KEY" \
"https://capydb.dev/api/capydb/v1/projects/$PROJECT_ID/audit-events?limit=50" \
| jq '.audit_events[] | {action, actor_kind, created_at}'There is no CLI command for the audit trail today.
Retention and durability
- No retention window is applied. Events are not pruned on a schedule; the trail persists for the life of the organization.
- Events survive project deletion. Deleting a project does not erase its audit history — the events remain attached to the organization (with the project reference cleared), so "who deleted it" is itself answerable afterwards.
- Audit writes on destructive paths are designed not to fail silently: if an audit write ever fails after a destructive action was already accepted, the miss is logged loudly on our side rather than swallowed.
Two trails, two jobs
The audit log records administrative actions against the platform. If you need to know what happened inside the database — slow queries, table activity — that is Observability's job. And if you want events pushed to you instead of queried, webhooks cover lifecycle outcomes.