
# Listar los registros de un usuario

`GET /v1/users/{user_id}/logs` · permiso `logs:read`

Lista los registros de un usuario con paginación por cursor, del más reciente al más antiguo. Son los datos que alimentan la vista de actividad del usuario en el panel. Sigue las mismas reglas que [Listar registros](/docs/api/management/logs/list), pero solo acepta los filtros `from`, `to`, `type` y `org_id`; los demás filtros se ignoran.

## Ámbito de organización

Envíe `org_id` para construir una vista de diagnóstico de un miembro destinada a un administrador de la organización. La clave también necesita el permiso `organizations:read`. Si el usuario no es miembro de esa organización (no tiene una membresía `invited`, `active` o `suspended`), la solicitud falla con `404 not_found`, la misma respuesta que para un usuario desconocido. Si el usuario es miembro, solo se devuelven los registros ocurridos en el contexto de esa organización, es decir, aquellos cuyo `org_id` es esa organización. Los inicios de sesión del usuario fuera de la organización, o en el contexto de otra organización, nunca se devuelven. Sin `org_id`, la vista abarca todos los registros del usuario en el entorno.

## Solicitud

| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
| `user_id` | ruta | sí | Id del usuario, con prefijo `usr_`. |
| `limit` | consulta | no | Tamaño de página, de 1 a 100. Por defecto 20. |
| `after` | consulta | no | Cursor opaco de `list_metadata.after` (registros más antiguos). Excluyente con `before`. |
| `before` | consulta | no | Cursor opaco de `list_metadata.before` (registros más recientes). Excluyente con `after`. |
| `from` | consulta | no | Hora RFC 3339; registros en esa hora o posteriores. Debe estar dentro de la ventana de retención. |
| `to` | consulta | no | Hora RFC 3339; registros anteriores a esa hora. Debe ser posterior a `from`. |
| `type` | consulta | no | Prefijo de tipo de registro, como `authentication.` o `session.created`. Repita el parámetro para hasta 20 valores; se devuelve un registro que coincida con cualquiera de ellos. |
| `org_id` | consulta | no | Id de la organización, con prefijo `org_`. Limita la vista a los registros de esa organización; consulte [Ámbito de organización](#ámbito-de-organización). Requiere `organizations:read`. |

Los registros siempre se ordenan por `occurred_at`, del más reciente al más antiguo; no existe un parámetro `order` (`order=desc` se acepta, cualquier otro valor falla con `validation_failed`). Un cursor solo es válido con los filtros para los que se emitió.

## Respuesta

`200 OK`. `data` contiene los registros de la página; `list_metadata.after` apunta a los registros más antiguos y `list_metadata.before` a los más recientes (`null` cuando no hay ninguno). No se devuelve un recuento total. Los campos vacíos son `null`.

`ip`, `ip_asn`, `user_agent` y `user_agent_family` solo están presentes cuando la clave tiene el permiso `audit-events:read_ip`, y `identifier_hint` solo cuando tiene `users:read_pii`. Sin el permiso, el campo se omite por completo (no se devuelve un valor enmascarado). `ip_country` siempre se devuelve.

```json
{
  "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>"
  }
}
```

## Errores

Cada error es un cuerpo `application/problem+json` con un `code` estable.

| HTTP | `code` | Cuándo |
|---|---|---|
| 400 | `invalid_cursor` | El cursor está mal formado o no se emitió para esta solicitud. |
| 400 | `invalid_filter` | El servicio de registros rechazó un filtro o el cursor. |
| 400 | `range_outside_retention` | `from` es anterior a la ventana de retención de su plan. |
| 422 | `validation_failed` | Un parámetro de consulta no es válido (`limit` fuera de rango, fecha y hora incorrecta, `to` no posterior a `from`, más de 20 valores de `type`, `org_id` mal formado, parámetro repetido). |
| 401 | `unauthenticated` | La clave de API falta, no es válida o fue revocada. |
| 403 | `insufficient_permissions` | La clave no tiene el permiso `logs:read`, o `org_id` está definido y la clave no tiene `organizations:read`. `required_permissions` enumera lo que necesita la solicitud. |
| 404 | `not_found` | El id del usuario está mal formado, o `org_id` está definido y el usuario no es miembro de esa organización. |
| 429 | `rate_limited` | Se superó el límite de frecuencia; respete `Retry-After`. |
| 503 | `service_unavailable` | Una dependencia no está disponible temporalmente; reintente más tarde. |

## Ejemplo

```bash
curl "https://api.eu.keenauth.com/v1/users/usr_0a1b2c3d4e5f6g7h8j9k0m1n2p/logs?limit=50" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY"
```

Solo los registros de una organización:

```bash
keenauth users logs list usr_0a1b2c3d4e5f6g7h8j9k0m1n2p --org-id org_01j9xk3m7q8r2s4t6v8w0y2z4d --type authentication.
```

## Datos personales

Este endpoint contiene datos personales: los registros del entorno. [Datos personales en las API de KeenAuth](/docs/policies/personal-data) describe qué se almacena, cuánto tiempo se conserva y cómo se elimina. No envíe categorías especiales de datos personales (salud, datos biométricos, religión, opiniones políticas, orientación sexual) en ningún campo.
