Audit Logs
Endpoint
Section titled “Endpoint”GET https://api.bve.me/admin/audit-logsAuthorization: Bearer admin_bve_YOUR_ADMIN_KEYReturns a paginated list of audit log entries. Every key lifecycle action (create, update, suspend, unsuspend, revoke, rotate, quota reset) is recorded as an audit log entry in D1.
Query parameters
Section titled “Query parameters”| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
target_id | string | No | — | Filter by the key UUID the action was taken on |
action | string | No | — | Filter by exact action type (e.g., api_key.created, api_key.revoked, api_key.quota_reset) |
action_category | string | No | — | Filter by action category: auth (login/logout events), keys (key lifecycle events), models (model allowlist events), or admins (admin user management events). Returns 400 validation_error for unrecognised values. |
actor_type | string | No | — | Filter by who performed the action. One of: admin (Bearer-token Admin API requests), admin_user (dashboard UI sessions). Returns 400 validation_error for other values. |
target_type | string | No | — | Filter by the type of resource the action targeted. One of: api_key, model, admin_user, model_allowlist. Returns 400 validation_error for other values. |
search | string | No | — | Free-text substring search across action, target_id, and actor_type fields (SQL LIKE match). Case-insensitive on most SQLite builds. |
since | string | No | — | Return only entries at or after this ISO 8601 timestamp (e.g., 2026-05-01T00:00:00Z). Passing an invalid timestamp returns 400 validation_error. |
until | string | No | — | Return only entries at or before this ISO 8601 timestamp. Combine with since for a precise date range. Passing an invalid timestamp returns 400 validation_error. |
limit | integer | No | 100 | Max entries to return (1–500) |
offset | integer | No | 0 | Number of entries to skip. Use with ?limit= for cursor-free pagination. |
Response (200)
Section titled “Response (200)”{ "total": 87, "has_more": true, "logs": [ { "id": "770e8400-e29b-41d4-a716-446655440002", "action": "api_key.created", "actor_type": "admin", "target_type": "api_key", "target_id": "550e8400-e29b-41d4-a716-446655440000", "metadata": { "name": "my-client", "source": "legacy_admin_api" }, "created_at": "2026-05-21T12:00:00.000Z" }, { "id": "880e8400-e29b-41d4-a716-446655440003", "action": "api_key.updated", "actor_type": "admin", "target_type": "api_key", "target_id": "550e8400-e29b-41d4-a716-446655440000", "metadata": { "changes": ["name", "rpm_limit"], "before": { "name": "old-name", "rpm_limit": 60 }, "after": { "name": "new-name", "rpm_limit": 120 }, "source": "legacy_admin_api" }, "created_at": "2026-05-21T13:00:00.000Z" } ]}Response fields
Section titled “Response fields”| Field | Type | Description |
|---|---|---|
total | integer | Total number of audit log entries matching the current filters (ignores limit/offset). Use for pagination UI. |
has_more | boolean | true when offset + len(logs) < total — indicates another page of results exists. |
logs | array | Array of audit log entry objects (see below). |
Log entry fields
Section titled “Log entry fields”| Field | Type | Description |
|---|---|---|
id | string | UUID of the audit log entry |
action | string | Action type (see below) |
actor_type | string | "admin" for Bearer-token Admin API operations; "admin_user" for dashboard actions |
target_type | string | "api_key" for key events; "admin_user" for admin user events; "model_allowlist" for allowlist events |
target_id | string | UUID or identifier of the affected resource (key UUID, admin user UUID, or model name) |
metadata | object | null | Parsed JSON with action-specific details (e.g., name, limits) |
created_at | string | ISO 8601 timestamp |
Action types
Section titled “Action types”Audit log entries are grouped into four categories. The action_category filter matches the
category heading, while ?action=<exact_action> matches a specific row.
keys — API key lifecycle (action_category=keys)
Section titled “keys — API key lifecycle (action_category=keys)”| Action | Trigger | actor_type | metadata fields |
|---|---|---|---|
api_key.created | POST /admin/api-keys | admin | name |
api_key.updated | PATCH /admin/api-keys/:id | admin | changes — array of changed field names (e.g. ["rpm_limit", "name"]); before — object with old values keyed by snake_case field name; after — object with new values keyed by snake_case field name |
api_key.rotated | POST /admin/api-keys/:id/rotate | admin | (none) |
api_key.suspended | POST /admin/api-keys/:id/suspend | admin | (none) |
api_key.unsuspended | POST /admin/api-keys/:id/unsuspend | admin | (none) |
api_key.revoked | POST /admin/api-keys/:id/revoke | admin | (none) |
api_key.quota_reset | POST /admin/api-keys/:id/reset-quota | admin | window — one of minute, day, month, all |
api_key.bulk_revoked | Dashboard bulk revoke | admin_user | actorId, actorEmail, ids (array of revoked UUIDs), count |
api_key.bulk_suspended | Dashboard bulk suspend | admin_user | actorId, actorEmail, ids (array of suspended UUIDs), count |
api_key.bulk_unsuspended | Dashboard bulk unsuspend | admin_user | actorId, actorEmail, ids (array of unsuspended UUIDs), count |
Single-key lifecycle entries triggered via the Bearer-token Admin API include source: "legacy_admin_api" in metadata. Bulk operation entries do not — they are always dashboard-initiated.
auth — Admin dashboard login events (action_category=auth)
Section titled “auth — Admin dashboard login events (action_category=auth)”| Action | Trigger | metadata fields |
|---|---|---|
admin.login | Successful dashboard login | actorEmail, ipAddress |
admin.logout | Dashboard logout | actorEmail |
models — Model allowlist changes (action_category=models)
Section titled “models — Model allowlist changes (action_category=models)”Actions in this category are generated by two paths — the Bearer-token Admin API and the dashboard UI — and use different action strings and actor_type values.
Admin API (actor_type: "admin"):
| Action | Trigger | metadata fields |
|---|---|---|
model_allowlist.added | POST /admin/model-allowlist — new model entry | enabled |
model_allowlist.updated | POST /admin/model-allowlist — existing model entry toggled | enabled |
model_allowlist.removed | DELETE /admin/model-allowlist/:model | enabled (value at time of deletion) |
Dashboard (actor_type: "admin_user"):
| Action | Trigger | metadata fields |
|---|---|---|
model_allowlist.update | Model entry created or toggled via dashboard UI | actorId, actorEmail, enabled |
model_allowlist.delete | Model entry removed via dashboard UI | actorId, actorEmail |
To retrieve all model allowlist changes regardless of source, use ?action_category=models. To isolate Admin API changes, add ?action=model_allowlist.added or ?action=model_allowlist.removed. To isolate dashboard changes, use ?action=model_allowlist.update.
admins — Admin user management (action_category=admins)
Section titled “admins — Admin user management (action_category=admins)”| Action | Trigger | metadata fields |
|---|---|---|
admin_user.create | New dashboard user created | actorId, actorEmail, email (new user’s email) |
admin_user.update | Dashboard user role or details changed | actorId, actorEmail |
admin_user.delete | Dashboard user deleted | actorId, actorEmail |
admin_user.password_changed | Dashboard user changed their own password | actorId, actorEmail |
cURL examples
Section titled “cURL examples”All logs
Section titled “All logs”curl "https://api.bve.me/admin/audit-logs" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Logs for a specific key
Section titled “Logs for a specific key”curl "https://api.bve.me/admin/audit-logs?target_id=550e8400-e29b-41d4-a716-446655440000" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Filter by action type
Section titled “Filter by action type”curl "https://api.bve.me/admin/audit-logs?action=api_key.rotated" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Filter by action category
Section titled “Filter by action category”Use ?action_category= to match a group of related actions without listing each one individually:
# All key lifecycle events (create/update/suspend/unsuspend/revoke/rotate/quota_reset)curl "https://api.bve.me/admin/audit-logs?action_category=keys" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"
# Auth events only (login, logout)curl "https://api.bve.me/admin/audit-logs?action_category=auth" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"
# Model allowlist events onlycurl "https://api.bve.me/admin/audit-logs?action_category=models" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"
# Admin user management events (create, update, delete, password change)curl "https://api.bve.me/admin/audit-logs?action_category=admins" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"
# Key lifecycle events for a specific keycurl "https://api.bve.me/admin/audit-logs?action_category=keys&target_id=550e8400-e29b-41d4-a716-446655440000" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Filter by actor type
Section titled “Filter by actor type”Use ?actor_type= to isolate events by who triggered them — the Bearer-token Admin API (admin) or the dashboard UI (admin_user):
# Only events triggered via the Bearer-token Admin APIcurl "https://api.bve.me/admin/audit-logs?actor_type=admin" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"
# Only events triggered via the dashboard UI (human operator or session-based action)curl "https://api.bve.me/admin/audit-logs?actor_type=admin_user" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Filter by target type
Section titled “Filter by target type”Use ?target_type= to restrict results to a specific resource category:
# Only events that affected API keyscurl "https://api.bve.me/admin/audit-logs?target_type=api_key" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"
# Only events that affected the model allowlistcurl "https://api.bve.me/admin/audit-logs?target_type=model_allowlist" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"
# Dashboard-initiated admin user eventscurl "https://api.bve.me/admin/audit-logs?target_type=admin_user&actor_type=admin_user" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Free-text search
Section titled “Free-text search”Use ?search= to match a substring across action, target_id, and actor_type fields:
# All entries containing "rotated"curl "https://api.bve.me/admin/audit-logs?search=rotated" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"
# Combine with since/until for a bounded search windowcurl "https://api.bve.me/admin/audit-logs?search=revoked&since=2026-05-01T00:00:00Z" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Entries since a timestamp
Section titled “Entries since a timestamp”curl "https://api.bve.me/admin/audit-logs?since=2026-05-01T00:00:00Z" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Entries within a date range
Section titled “Entries within a date range”Combine since and until to query a bounded time window:
curl "https://api.bve.me/admin/audit-logs?since=2026-05-01T00:00:00Z&until=2026-05-31T23:59:59Z" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Most recent 10 entries
Section titled “Most recent 10 entries”curl "https://api.bve.me/admin/audit-logs?limit=10" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"Paginate through logs
Section titled “Paginate through logs”Use ?limit= and ?offset= together for cursor-free pagination:
# Page 1 — first 50 entriescurl "https://api.bve.me/admin/audit-logs?limit=50&offset=0" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"
# Page 2 — next 50 entriescurl "https://api.bve.me/admin/audit-logs?limit=50&offset=50" \ -H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"CLI Reference
Section titled “CLI Reference”bun run audit-logs wraps this endpoint with all server-side filters and human-readable output.
# All recent events (local dev server)bun run audit-logs
# Productionbun run audit-logs -- --remote
# Filter by date rangebun run audit-logs -- --since 2026-01-01 --until 2026-05-31
# Filter by action categorybun run audit-logs -- --action-category keys # key lifecycle eventsbun run audit-logs -- --action-category auth # login/logout events
# Filter by exact actionbun run audit-logs -- --action api_key.rotated
# Filter by actor typebun run audit-logs -- --actor-type admin_user # dashboard sessions
# Filter by target resourcebun run audit-logs -- --target-type api_key --target-id <uuid>
# Text searchbun run audit-logs -- --search "revoked"
# Paginatebun run audit-logs -- --limit 50 --offset 100
# Machine-readable JSONbun run audit-logs -- --jsonCommon errors
Section titled “Common errors”| Status | Code | Cause |
|---|---|---|
| 400 | validation_error | action_category is not one of auth, keys, models, admins |
| 400 | validation_error | actor_type is not one of admin, admin_user |
| 400 | validation_error | target_type is not one of api_key, model, admin_user, model_allowlist |
| 400 | validation_error | since or until is not a parseable ISO 8601 timestamp |
| 401 | missing_api_key | No Authorization header |
| 401 | invalid_api_key | Admin key does not match the configured ADMIN_API_KEY secret |
- Audit log entries are immutable. They cannot be deleted or modified via the API.
- Entries are returned in descending chronological order (most recent first).
- Rate limit events are not in this log. They are sent to the EVENTS_QUEUE as
type: "alert"events and optionally delivered to a configuredWEBHOOK_URL.