List a user's logs
GET /v1/users/{user_id}/logs · permission logs:read
Lists the log records of one user with cursor pagination, newest first. This is the data behind the dashboard's user activity view. It follows the same rules as List logs but accepts only the from, to, type and org_id filters; other filters are ignored.
Organization scope
Pass org_id to build a member diagnostics view for an organization admin. The key then also needs the organizations:read permission. If the user is not a member of that organization (no invited, active or suspended membership), the request fails with 404 not_found, the same response as for an unknown user. If the user is a member, only records that happened in the context of that organization are returned: records whose org_id is that organization. The user's sign-ins outside the organization, or in the context of another organization, are never returned. Without org_id the view covers every record of the user in the environment.
Request
| Parameter | In | Required | Description |
|---|---|---|---|
user_id |
path | yes | User id, prefixed usr_. |
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. |
from |
query | no | RFC 3339 time; records at or after it. Must be inside the retention window. |
to |
query | no | RFC 3339 time; records before it. Must be later than from. |
type |
query | no | Log type prefix such as authentication. or session.created. Repeat the parameter for up to 20 values; a record that matches any of them is returned. |
org_id |
query | no | Organization id, prefixed org_. Limits the view to records of that organization; see Organization scope. Needs organizations:read. |
Records are always ordered by occurred_at, 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 it was issued for.
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). No total count is returned. Empty fields are null.
ip, ip_asn, user_agent and user_agent_family are present only when the key has the audit-events:read_ip permission, and identifier_hint only when it has users:read_pii. Without the permission the field is left out entirely (no masked value). ip_country is always returned.
{
"data": [
{
"id": "evt_01j9xk3m7q8r2s4t6v8w0y2z4a",
"occurred_at": "2026-10-01T08:15:42.120Z",
"type": "authentication.password.failed",
"outcome": "failure",
"reason_code": "invalid_credentials",
"user_id": "usr_0a1b2c3d4e5f6g7h8j9k0m1n2p",
"org_id": null,
"application_id": "app_01j9xk3m7q8r2s4t6v8w0y2z4b",
"connection_id": "conn_01j9xk3m7q8r2s4t6v8w0y2z4c",
"directory_id": null,
"session_id": null,
"identifier_hint": "a***@e***.com",
"ip": "203.0.113.7",
"ip_country": "DE",
"ip_asn": 64500,
"user_agent": "Mozilla/5.0 (X11; Linux x86_64)",
"user_agent_family": "Firefox",
"risk": null,
"auth_context": null,
"act_chain": null,
"details": {"attempt": 3},
"request_id": "0192a6c1-7d3e-7a10-9b2c-3d4e5f607182",
"correlation_id": null,
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
],
"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 log service rejected a filter or the cursor. |
| 400 | range_outside_retention |
from is earlier than the retention window of your plan. |
| 422 | validation_failed |
A query parameter is invalid (limit out of range, bad timestamp, to not after from, more than 20 type values, malformed org_id, repeated parameter). |
| 401 | unauthenticated |
The API key is missing, invalid or revoked. |
| 403 | insufficient_permissions |
The key lacks the logs:read permission, or org_id is set and the key lacks organizations:read. required_permissions lists what the request needs. |
| 404 | not_found |
The user id is malformed, or org_id is set and the user is not a member of that organization. |
| 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/users/usr_0a1b2c3d4e5f6g7h8j9k0m1n2p/logs?limit=50" \
-H "Authorization: Bearer $KEENAUTH_SECRET_KEY"
Only the records of one organization:
keenauth users logs list usr_0a1b2c3d4e5f6g7h8j9k0m1n2p --org-id org_01j9xk3m7q8r2s4t6v8w0y2z4d --type authentication.
Personal data
This endpoint carries personal data: environment logs. 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.