
# Listar eventos

`GET /v1/events` · permiso `events:read`

Devuelve los eventos del entorno en orden, del más antiguo al más reciente. Cada evento es el mismo documento, con el mismo `id`, que entregan los webhooks. Los eventos se conservan durante 30 días en todos los planes.

Use este endpoint cuando su sistema no pueda recibir webhooks, o para cubrir un hueco tras una interrupción: guarde el último `next_cursor` procesado y continúe desde él.

El entorno siempre proviene de la clave de API; no existe un parámetro de entorno.

## Solicitud

| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
| `after` | query | no | `next_cursor` de la respuesta anterior, o `latest` para recibir solo los eventos registrados a partir de ahora. Sin él, la lista comienza en el evento más antiguo conservado. |
| `types` | query | no | Tipos de evento separados por comas, máximo 20, por ejemplo `user.created,user.deleted`. Sin él se devuelven todos los tipos. |
| `org_id` | query | no | Solo eventos de esta organización (`org_…`). |
| `limit` | query | no | Tamaño de página, de 1 a 100. Por defecto 20. |
| `payload` | query | no | `thin` (por defecto) o `full`. `full` también requiere el permiso `users:read`. Mientras la carga completa no esté disponible, `full` devuelve la carga reducida. |
| `purpose` | query | no | `tail` (por defecto) o `webhooks_listen`. `webhooks_listen` se acepta solo en los entornos `development` y `staging`. |

## Respuesta

`200 OK`:

| Campo | Descripción |
|---|---|
| `data` | Eventos en orden. Cada evento tiene un `seq` que aumenta dentro del entorno. |
| `next_cursor` | Siempre presente. Envíelo como `after` en la siguiente solicitud, incluso cuando `data` esté vacío. |
| `has_more` | `true` cuando hay más eventos; vuelva a solicitar de inmediato. Cuando es `false`, espere antes de volver a consultar (de 5 a 60 segundos). |

El cursor no depende de `types` ni de `org_id`: puede cambiar el filtro y continuar desde la misma posición. Un cursor solo funciona en el entorno que lo emitió.

```json
{
  "data": [
    {
      "id": "evt_01j9xk3m7q8r2s4t6v8w0y2z4a",
      "type": "connection.deleted",
      "schema_version": 1,
      "environment_id": "env_01j9xk3m7q8r2s4t6v8w0y2z4e",
      "occurred_at": "2026-11-03T08:15:42.120Z",
      "recorded_at": "2026-11-03T08:15:42.140Z",
      "seq": 4182,
      "data": {
        "object": {"id": "conn_01j9xk3m7q8r2s4t6v8w0y2z4f", "status": "deleted"},
        "object_version": 2
      },
      "context": {"actor": {"type": "system"}, "request_id": "0192a6c1-7d3e-7a10-9b2c-3d4e5f607182"}
    }
  ],
  "next_cursor": "eyJ2IjoxfQ.c2lnbmF0dXJl",
  "has_more": false
}
```

## Ejemplo: sondear nuevos eventos

```bash
cursor=latest
while true; do
  page=$(curl -sS -G https://api.keenauth.com/v1/events \
    -H "Authorization: Bearer $KEENAUTH_API_KEY" \
    --data-urlencode "after=$cursor" --data-urlencode "limit=100")
  echo "$page" | jq -c '.data[]'
  cursor=$(echo "$page" | jq -r '.next_cursor')
  [ "$(echo "$page" | jq -r '.has_more')" = "true" ] || sleep 10
done
```

La CLI hace lo mismo con `keenauth events tail`.

## 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 se emitió en otro entorno. |
| 401 | `unauthenticated` | La clave de API falta, no es válida o fue revocada. |
| 403 | `insufficient_permissions` | La clave no tiene `events:read`, o `payload=full` sin `users:read`. |
| 403 | `test_feature_not_in_production` | `purpose=webhooks_listen` en un entorno de producción. |
| 422 | `validation_failed` | Un parámetro no es válido o está repetido; `errors[].pointer` lo indica. |
| 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. |

## Datos personales

Este endpoint contiene datos personales: eventos. [Datos personales en las API de KeenAuth](/docs/policies/personal-data) describe qué se almacena, durante 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.
