Appearance
Audit Events
Lumi emits security audit events as structured JSON to stdout. In BYOC deployments, the customer is responsible for log shipping, SIEM ingestion, retention, tamper protection, and access control for stored logs. Lumi is responsible for event emission and the stable schema documented here.
Transport
lumi-apiemits audit events through theno.lumi.auditlogger.lumi-dashboardemits audit events through a dedicated server-side pino logger.- General log-level knobs must not silence audit events.
- Audit examples below are synthetic and do not contain production identifiers.
Schema
Current schema version: lumi.audit.v1.
Required fields:
| Field | Description |
|---|---|
audit_schema | Schema version, currently lumi.audit.v1 |
audit_event | Stable event id from the catalog below |
audit_category | auth, admin, api_key, export, or delete |
audit_operation | Operation verb such as create, delete, or authorize |
audit_outcome | success, denied, not_found, conflict, or failure |
actor_type | oidc_user, api_key, anonymous, or system |
resource_type | Resource category for the event |
Optional fields include actor_user_ref, actor_key_id, actor_is_org_admin, org_id, org_slug, team_id, team_slug, resource_id, resource_id_pseudonymized, resource_count, route_template, and request_method.
actor_user_ref and pseudonymized resource ids use the same deterministic pseudonym in API and dashboard logs: id: plus the first 12 lowercase hexadecimal characters of SHA-256 over the UTF-8 identifier.
Privacy Rules
Audit events must not contain bearer tokens, API key values, API key hashes, session ids, encrypted cookie values, emails, display names, raw OIDC subjects, raw OIDC group ids, raw feedback text, raw answer values, raw export filter values, or raw customer-controlled surveyId values.
When an identifier is customer-controlled, the event uses a pseudonymized resource_id and sets resource_id_pseudonymized=true.
Event Catalog
| Event | Category | Meaning |
|---|---|---|
auth.oidc.success | auth | Dashboard OIDC callback created a server-side session |
auth.oidc.denied | auth | API rejected an invalid bearer token |
auth.oidc.failure | auth | API OIDC validation failed unexpectedly |
auth.org_admin.denied | auth | Org-admin authorization rejected a caller |
auth.team.denied | auth | Team authorization rejected a caller |
auth.team.explicit_team_missing | auth | Route required ?team= and it was absent |
admin.org.update | admin | Organization metadata changed |
admin.team.create | admin | Team created |
admin.group_mapping.create | admin | OIDC group mapping created |
admin.group_mapping.delete | admin | OIDC group mapping deleted |
admin.survey_definition.delete | admin | Survey definition deleted |
api_key.create | api_key | API key created |
api_key.rotate | api_key | API key rotated |
api_key.revoke | api_key | API key revoked |
export.feedback | export | Feedback export generated |
feedback.delete | delete | Single feedback item deleted |
survey_feedback.delete | delete | Feedback and markers for a survey deleted |
marker.delete | delete | Rating marker deleted |
tag.delete | delete | Feedback tag deleted |
API Key Event Read Surface
Dashboard clients can read the API key lifecycle trail for an authorized team:
http
GET /api/v1/dashboard/api-keys/events?team={teamSlug}&keyId={uuid?}&limit={1..100?}The response is additive and reserves cursor pagination for a later release:
json
{
"events": [
{
"id": "00000000-0000-0000-0000-000000000001",
"keyId": "00000000-0000-0000-0000-000000000002",
"keyPrefix": "lumi_pk_live",
"keyType": "pk",
"eventType": "created",
"actor": {
"type": "oidc_user",
"userRef": "id:fcdec6df4d44",
"keyId": null
},
"createdAt": "2026-06-13T12:00:00Z"
}
],
"nextBefore": null
}Org admins can read events for all keys in the authorized team. Non-admin users can read only pk key events. If a non-admin requests an invisible sk key by id, the response returns an empty event list rather than confirming whether the key exists.
