List audit events
GET /v1/audit-events · permission audit-events:read
Lists the environment's audit records with cursor pagination, newest first. The admin stream holds changes made through the management API, the dashboard and the platform; the application stream holds events your application wrote. Every record is part of a hash chain per environment and stream, so the history can be verified independently with an export (POST /v1/audit-exports).
The environment always comes from the API key; there is no environment parameter.
Request
| Parameter | In | Required | Description |
|---|---|---|---|
limit |
query | no | Page size, 1 to 100. Default 20. |
after |
query | no | Opaque cursor from list_metadata.after (older records). Exclusive with before. |
before |
query | no | Opaque cursor from list_metadata.before (newer records). Exclusive with after. |
stream |
query | no | admin (default) or application. |
from |
query | no | RFC 3339 time; records at or after it. Defaults to the later of the start of the retention window and to minus 92 days. |
to |
query | no | RFC 3339 time; records before it. Defaults to now. The range from from to to is at most 92 days. |
action |
query | no | Action prefix such as user. or connection.updated. |
category |
query | no | authentication, session, token, administration, provisioning, federation, authorization, data_access, security, key_management, configuration or application. |
actor_type |
query | no | Actor type such as api_key, dashboard_user, m2m_client, agent or system. |
actor_id |
query | no | Exact actor id as recorded (key_…, usr_…, system:<service>). |
target_type |
query | no | Target type such as user, organization or audit_export. |
target_id |
query | no | Exact target id. With target_type, both must match the same target. |
org_id |
query | no | Records about one organization (org_…). |
outcome |
query | no | success, failure, denied or error. |
request_id |
query | no | Idp-Request-Id of a request (UUID). |
correlation_id |
query | no | Idp-Correlation-Id of a workflow (UUID). |
agent_only |
query | no | true returns only agent actions: actor_type agent or a non-empty act_chain. |
client |
query | no | The kind of client that made the call: mcp (the management MCP server, metadata.client = mcp/<version>) or cli (the KeenAuth CLI, cli/<version>). The client declares this value itself; it is not proof of who acted, the record's actor is. List only: an export has no client filter. |
Records are always ordered by occurred_at (then id), newest first; there is no order parameter (order=desc is accepted, any other value fails with validation_failed). A cursor is only valid with the filters and the environment it was issued for. No total count is returned.
A search that takes longer than 10 seconds stops with 422 search_too_broad: narrow the filters or the time range, or use an export for large ranges.
Response
200 OK. data holds the records on the page; list_metadata.after points to older records and list_metadata.before to newer ones (null when there are none). Empty fields are null.
ip and user_agent are present only when the key has the audit-events:read_ip permission, and are filled only for records younger than 13 months. Without the permission the fields are left out entirely (no masked value). ip_prefix, ip_country, ip_asn and user_agent_family are always returned. prev_hash, org_prev_hash and record_hash are unpadded base64url SHA-256 values.
{
"data": [
{
"id": "evt_01j9xk3m7q8r2s4t6v8w0y2z4a",
"stream": "admin",
"seq": 4182,
"schema_version": 1,
"action": "user.blocked",
"category": "administration",
"outcome": "success",
"reason_code": null,
"occurred_at": "2026-10-01T08:15:42.120Z",
"received_at": "2026-10-01T08:15:42.131Z",
"recorded_at": "2026-10-01T08:15:42.140Z",
"clock_anomaly": false,
"actor_type": "api_key",
"actor_id": "key_01j9xk3m7q8r2s4t6v8w0y2z4d",
"act_chain": null,
"session_id": null,
"auth_context": null,
"elevation_ref": null,
"org_id": null,
"targets": [
{"type": "user", "id": "usr_0a1b2c3d4e5f6g7h8j9k0m1n2p", "org_id": null, "name": null}
],
"application_id": null,
"ip_prefix": "203.0.113.0/24",
"ip_country": "DE",
"ip_asn": 64500,
"user_agent_family": "keenauth-go",
"request_id": "0192a6c1-7d3e-7a10-9b2c-3d4e5f607182",
"correlation_id": null,
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"producer_service": "directory",
"policy_ref": "perm:users:write",
"changes": {"status": {"old": "active", "new": "blocked"}},
"metadata": null,
"prev_hash": "n4bQgYhMfWWaL-qgxVrQFaO_TxsrC4Is0V1sFbDwCgg",
"org_prev_hash": null,
"record_hash": "LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564"
}
],
"list_metadata": {
"before": null,
"after": "<opaque cursor>"
}
}
Errors
Every error is an application/problem+json body with a stable code.
| HTTP | code |
When |
|---|---|---|
| 400 | invalid_cursor |
The cursor is malformed or was not issued for this request. |
| 400 | invalid_filter |
The audit service rejected a filter, for example a range longer than 92 days. |
| 400 | range_outside_retention |
from or to is outside the audit retention window of your plan. |
| 422 | validation_failed |
A query parameter is invalid (limit out of range, bad timestamp, to not after from, unknown stream, category or outcome, malformed id, repeated parameter, order other than desc). |
| 422 | search_too_broad |
The search took longer than 10 seconds; narrow it. |
| 401 | unauthenticated |
The API key is missing, invalid or revoked. |
| 403 | insufficient_permissions |
The key lacks audit-events:read. |
| 429 | rate_limited |
The rate limit was exceeded; honor Retry-After. |
| 503 | service_unavailable |
A dependency is temporarily unavailable; retry later. |
Example
curl "https://api.eu.keenauth.com/v1/audit-events?action=user.&from=2026-10-01T00:00:00Z" \
-H "Authorization: Bearer $KEENAUTH_SECRET_KEY"
keenauth audit-events list --action user. --from 2026-10-01T00:00:00Z
Personal data
This endpoint carries personal data: audit events. Personal data in the KeenAuth APIs describes what is stored, how long it is kept and how it is deleted. Do not send special categories of personal data (health, biometric data, religion, political opinions, sexual orientation) in any field.