# Eventos de seguridad

> Consulta los eventos de seguridad de una zona, con filtros, resúmenes, histogramas y un stream en directo (Server-Sent Events).

Los eventos de seguridad son las peticiones que el borde bloqueó, desafió o registró (WAF, reglas, límites de peticiones, escudos). Todas las rutas comparten los mismos filtros; cada filtro se puede repetir y filtros distintos se combinan con Y.

| Operación | Ruta |
|---|---|
| [Listar eventos](https://developers.openwaf.dev/es/api/security-events/#list-events) | `GET /v1/orgs/{org}/zones/{zone}/events` |
| [Resumir eventos](https://developers.openwaf.dev/es/api/security-events/#get-events-summary) | `GET /v1/orgs/{org}/zones/{zone}/events/summary` |
| [Histograma de eventos](https://developers.openwaf.dev/es/api/security-events/#get-events-histogram) | `GET /v1/orgs/{org}/zones/{zone}/events/histogram` |
| [Stream de eventos](https://developers.openwaf.dev/es/api/security-events/#stream-events) | `GET /v1/orgs/{org}/zones/{zone}/events/stream` |

## Listar eventos

```http
GET /v1/orgs/{org}/zones/{zone}/events
```

Lista los eventos de seguridad de la zona, del más reciente al más antiguo. Cada filtro se puede repetir (`action=block&action=log` admite cualquiera de los valores) con como mucho 20 valores; filtros distintos se combinan con Y, y los filtros `not_…` excluyen los eventos con cualquiera de sus valores. `q` busca texto (sin distinguir mayúsculas) en el nombre, la ruta, la IP, el método, la regla, el tipo de ataque, el mensaje, el user agent, el país, el id de regla y el estado. Para paginar, pasa `next_cursor` en `cursor`.

**Rol necesario:** `readonly` o superior.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |
| `zone` | ruta | string (uuid) | Sí | Id de la zona (consulta [Listar zonas](https://developers.openwaf.dev/es/api/zones/#list-zones)). |
| `since` | consulta | string (date-time) | No | RFC 3339; por defecto until - 24 h. |
| `until` | consulta | string (date-time) | No | RFC 3339, exclusivo; por defecto ahora. Rango máximo 31 días. |
| `action` | consulta | string | No | Repetible (cualquiera de los valores, como mucho 20). Uno de `block`, `log`, `challenge`, `allow`. |
| `rule_id` | consulta | integer | No | Repetible. De 0 a 4294967295. |
| `ip` | consulta | string | No | IPv4 o IPv6 (se normaliza); repetible. |
| `country` | consulta | string | No | ISO 3166-1 alfa-2 (se admite en minúsculas); repetible. Patrón `^[A-Za-z]{2}$`. |
| `host` | consulta | string | No | Nombre exacto (sin distinguir mayúsculas); repetible. De 1 a 253 caracteres. |
| `path` | consulta | string | No | Ruta exacta (1-2048 bytes); repetible. De 1 a 2048 caracteres. |
| `status` | consulta | integer | No | Estado HTTP de la respuesta; repetible. De 100 a 599. |
| `rate_rule` | consulta | string | No | Eventos de esa regla de límite de peticiones (rule_name `rate_rule:<id>`, attack_type `rate_limit`); repetible. Patrón `^[a-z0-9][a-z0-9-]{0,39}$`. |
| `rule_name` | consulta | string | No | rule_name exacto (el código de un bloqueo de egress, p. ej. `injection_detected`); repetible. De 1 a 200 caracteres. |
| `not_action` | consulta | string | No | Excluye esas acciones; repetible. Uno de `block`, `log`, `challenge`, `allow`. |
| `not_rule_id` | consulta | integer | No | Excluye esas reglas; repetible. De 0 a 4294967295. |
| `not_ip` | consulta | string | No | Excluye esas IP; repetible. |
| `not_country` | consulta | string | No | Excluye esos países; repetible. Patrón `^[A-Za-z]{2}$`. |
| `not_host` | consulta | string | No | Excluye esos nombres; repetible. De 1 a 253 caracteres. |
| `not_path` | consulta | string | No | Excluye esas rutas exactas; repetible. De 1 a 2048 caracteres. |
| `not_status` | consulta | integer | No | Excluye esos estados HTTP; repetible. De 100 a 599. |
| `not_rate_rule` | consulta | string | No | Excluye los eventos de esas reglas de límite de peticiones; repetible. Patrón `^[a-z0-9][a-z0-9-]{0,39}$`. |
| `not_rule_name` | consulta | string | No | Excluye esos rule_name; repetible. De 1 a 200 caracteres. |
| `q` | consulta | string | No | Texto libre (sin distinguir mayúsculas) en host, ruta, IP, método, rule_name, attack_type, msg, user agent, país, rule_id y status. Como mucho 200 caracteres. |
| `limit` | consulta | integer | No | Número máximo de elementos. Por defecto `100`. De 1 a 1000. |
| `cursor` | consulta | string | No | next_cursor de la página anterior. |

#### Respuesta

- `200`: Devuelve un objeto con los campos de abajo.

| Campo | Tipo | Descripción |
|---|---|---|
| `items` | array de [EdgeEvent](https://developers.openwaf.dev/es/api/schemas/#edge-event) |  |
| `next_cursor` | string |  |

#### Errores

| Estado | Descripción |
|---|---|
| `400` | Petición mal formada (`bad_request`). |
| `404` | No existe o el token no lo ve (`not_found`). |
| `503` | No disponible ahora (`unavailable`); reinténtalo más tarde. |

#### Ejemplo

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/events?action=block&limit=50" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.listEvents({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  action: "block",
  limit: 50
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.list_events(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d", action="block", limit=50)
```



```json
{
  "items": [
    {
      "event_id": "8b3e5f7a-1c2d-4e6f-9a0b-3c5d7e9f1a2b",
      "zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
      "node_id": "edge-node",
      "ts": "2026-10-27T09:29:58Z",
      "host": "www.example.com",
      "ip": "203.0.113.10",
      "method": "POST",
      "path": "/login",
      "status": 429,
      "action": "block",
      "rule_id": 0,
      "rule_name": "rate_rule:login",
      "attack_type": "rate_limit",
      "msg": "",
      "country_code": "ES",
      "asn": 64500,
      "user_agent": "Mozilla/5.0",
      "tls_fingerprint": "",
      "received_at": "2026-10-27T09:29:59Z",
      "rate_rule": "login"
    }
  ],
  "next_cursor": ""
}
```

## Resumir eventos

```http
GET /v1/orgs/{org}/zones/{zone}/events/summary
```

Cuenta los eventos que cumplen los filtros (los mismos que [Listar eventos](https://developers.openwaf.dev/es/api/security-events/#list-events)) por acción, con las 10 reglas, los 10 países y las 10 reglas de límite de peticiones con más eventos.

**Rol necesario:** `readonly` o superior.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |
| `zone` | ruta | string (uuid) | Sí | Id de la zona (consulta [Listar zonas](https://developers.openwaf.dev/es/api/zones/#list-zones)). |
| `since` | consulta | string (date-time) | No | RFC 3339; por defecto until - 24 h. |
| `until` | consulta | string (date-time) | No | RFC 3339, exclusivo; por defecto ahora. Rango máximo 31 días. |
| `action` | consulta | string | No | Repetible (cualquiera de los valores, como mucho 20). Uno de `block`, `log`, `challenge`, `allow`. |
| `rule_id` | consulta | integer | No | Repetible. De 0 a 4294967295. |
| `ip` | consulta | string | No | IPv4 o IPv6 (se normaliza); repetible. |
| `country` | consulta | string | No | ISO 3166-1 alfa-2 (se admite en minúsculas); repetible. Patrón `^[A-Za-z]{2}$`. |
| `host` | consulta | string | No | Nombre exacto (sin distinguir mayúsculas); repetible. De 1 a 253 caracteres. |
| `path` | consulta | string | No | Ruta exacta (1-2048 bytes); repetible. De 1 a 2048 caracteres. |
| `status` | consulta | integer | No | Estado HTTP de la respuesta; repetible. De 100 a 599. |
| `rate_rule` | consulta | string | No | Eventos de esa regla de límite de peticiones (rule_name `rate_rule:<id>`, attack_type `rate_limit`); repetible. Patrón `^[a-z0-9][a-z0-9-]{0,39}$`. |
| `rule_name` | consulta | string | No | rule_name exacto (el código de un bloqueo de egress, p. ej. `injection_detected`); repetible. De 1 a 200 caracteres. |
| `not_action` | consulta | string | No | Excluye esas acciones; repetible. Uno de `block`, `log`, `challenge`, `allow`. |
| `not_rule_id` | consulta | integer | No | Excluye esas reglas; repetible. De 0 a 4294967295. |
| `not_ip` | consulta | string | No | Excluye esas IP; repetible. |
| `not_country` | consulta | string | No | Excluye esos países; repetible. Patrón `^[A-Za-z]{2}$`. |
| `not_host` | consulta | string | No | Excluye esos nombres; repetible. De 1 a 253 caracteres. |
| `not_path` | consulta | string | No | Excluye esas rutas exactas; repetible. De 1 a 2048 caracteres. |
| `not_status` | consulta | integer | No | Excluye esos estados HTTP; repetible. De 100 a 599. |
| `not_rate_rule` | consulta | string | No | Excluye los eventos de esas reglas de límite de peticiones; repetible. Patrón `^[a-z0-9][a-z0-9-]{0,39}$`. |
| `not_rule_name` | consulta | string | No | Excluye esos rule_name; repetible. De 1 a 200 caracteres. |
| `q` | consulta | string | No | Texto libre (sin distinguir mayúsculas) en host, ruta, IP, método, rule_name, attack_type, msg, user agent, país, rule_id y status. Como mucho 200 caracteres. |

#### Respuesta

- `200`: Devuelve un objeto con los campos de abajo.

| Campo | Tipo | Descripción |
|---|---|---|
| `since` | string (date-time) |  |
| `until` | string (date-time) |  |
| `total` | integer |  |
| `by_action` | array de [KeyCount](https://developers.openwaf.dev/es/api/schemas/#key-count) |  |
| `top_rules` | array de object |  |
| `top_rules[].rule_id` | integer | Id de la regla. |
| `top_rules[].rule_name` | string | Nombre o código de la regla. |
| `top_rules[].count` | integer |  |
| `top_countries` | array de [KeyCount](https://developers.openwaf.dev/es/api/schemas/#key-count) |  |
| `top_rate_rules` | array de [KeyCount](https://developers.openwaf.dev/es/api/schemas/#key-count) | Eventos de reglas de límite de peticiones por id de regla (key). |

#### Errores

| Estado | Descripción |
|---|---|
| `400` | Petición mal formada (`bad_request`). |
| `404` | No existe o el token no lo ve (`not_found`). |
| `503` | No disponible ahora (`unavailable`); reinténtalo más tarde. |

#### Ejemplo

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/events/summary" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.getEventsSummary({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.get_events_summary(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "since": "2026-10-27T09:30:00Z",
  "until": "2026-10-27T09:30:00Z",
  "total": 128,
  "by_action": [
    {
      "key": "/pricing",
      "count": 128
    }
  ],
  "top_rules": [
    {
      "rule_id": 0,
      "rule_name": "example",
      "count": 128
    }
  ],
  "top_countries": [
    {
      "key": "/pricing",
      "count": 128
    }
  ],
  "top_rate_rules": [
    {
      "key": "/pricing",
      "count": 128
    }
  ]
}
```

## Histograma de eventos

```http
GET /v1/orgs/{org}/zones/{zone}/events/histogram
```

Cuenta los eventos que cumplen los filtros por intervalo de tiempo y acción. Las barras de `step` segundos se alinean a múltiplos de `step` desde la época Unix, sin huecos (las vacías cuentan 0); la primera contiene `since` y la última empieza antes de `until`.

**Rol necesario:** `readonly` o superior.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |
| `zone` | ruta | string (uuid) | Sí | Id de la zona (consulta [Listar zonas](https://developers.openwaf.dev/es/api/zones/#list-zones)). |
| `since` | consulta | string (date-time) | No | RFC 3339; por defecto until - 24 h. |
| `until` | consulta | string (date-time) | No | RFC 3339, exclusivo; por defecto ahora. Rango máximo 31 días. |
| `step` | consulta | integer | No | Segundos por barra (1-86400). Por defecto el menor de 1, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 10800, 21600, 43200 y 86400 que da como mucho 60 barras; con un step explícito, más de 1000 barras: 400. De 1 a 86400. |
| `action` | consulta | string | No | Repetible (cualquiera de los valores, como mucho 20). Uno de `block`, `log`, `challenge`, `allow`. |
| `rule_id` | consulta | integer | No | Repetible. De 0 a 4294967295. |
| `ip` | consulta | string | No | IPv4 o IPv6 (se normaliza); repetible. |
| `country` | consulta | string | No | ISO 3166-1 alfa-2 (se admite en minúsculas); repetible. Patrón `^[A-Za-z]{2}$`. |
| `host` | consulta | string | No | Nombre exacto (sin distinguir mayúsculas); repetible. De 1 a 253 caracteres. |
| `path` | consulta | string | No | Ruta exacta (1-2048 bytes); repetible. De 1 a 2048 caracteres. |
| `status` | consulta | integer | No | Estado HTTP de la respuesta; repetible. De 100 a 599. |
| `rate_rule` | consulta | string | No | Eventos de esa regla de límite de peticiones (rule_name `rate_rule:<id>`, attack_type `rate_limit`); repetible. Patrón `^[a-z0-9][a-z0-9-]{0,39}$`. |
| `rule_name` | consulta | string | No | rule_name exacto (el código de un bloqueo de egress, p. ej. `injection_detected`); repetible. De 1 a 200 caracteres. |
| `not_action` | consulta | string | No | Excluye esas acciones; repetible. Uno de `block`, `log`, `challenge`, `allow`. |
| `not_rule_id` | consulta | integer | No | Excluye esas reglas; repetible. De 0 a 4294967295. |
| `not_ip` | consulta | string | No | Excluye esas IP; repetible. |
| `not_country` | consulta | string | No | Excluye esos países; repetible. Patrón `^[A-Za-z]{2}$`. |
| `not_host` | consulta | string | No | Excluye esos nombres; repetible. De 1 a 253 caracteres. |
| `not_path` | consulta | string | No | Excluye esas rutas exactas; repetible. De 1 a 2048 caracteres. |
| `not_status` | consulta | integer | No | Excluye esos estados HTTP; repetible. De 100 a 599. |
| `not_rate_rule` | consulta | string | No | Excluye los eventos de esas reglas de límite de peticiones; repetible. Patrón `^[a-z0-9][a-z0-9-]{0,39}$`. |
| `not_rule_name` | consulta | string | No | Excluye esos rule_name; repetible. De 1 a 200 caracteres. |
| `q` | consulta | string | No | Texto libre (sin distinguir mayúsculas) en host, ruta, IP, método, rule_name, attack_type, msg, user agent, país, rule_id y status. Como mucho 200 caracteres. |

#### Respuesta

- `200`: Devuelve [EventsHistogram](https://developers.openwaf.dev/es/api/schemas/#events-histogram).

| Campo | Tipo | Descripción |
|---|---|---|
| `since` | string (date-time) |  |
| `until` | string (date-time) |  |
| `step` | integer | Segundos por barra. |
| `total` | integer | Eventos de [since, until). |
| `buckets` | array de object |  |
| `buckets[].ts` | string (date-time) | Inicio de la barra. |
| `buckets[].count` | integer |  |
| `buckets[].block` | integer |  |
| `buckets[].log` | integer |  |
| `buckets[].challenge` | integer |  |
| `buckets[].allow` | integer |  |

#### Errores

| Estado | Descripción |
|---|---|
| `400` | Petición mal formada (`bad_request`). |
| `404` | No existe o el token no lo ve (`not_found`). |
| `503` | No disponible ahora (`unavailable`); reinténtalo más tarde. |

#### Ejemplo

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/events/histogram" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.getEventsHistogram({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.get_events_histogram(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "since": "2026-10-27T09:30:00Z",
  "until": "2026-10-27T09:30:00Z",
  "step": 0,
  "total": 128,
  "buckets": [
    {
      "ts": "2026-10-27T09:30:00Z",
      "count": 128,
      "block": 0,
      "log": 0,
      "challenge": 0,
      "allow": 0
    }
  ]
}
```

## Stream de eventos

```http
GET /v1/orgs/{org}/zones/{zone}/events/stream
```

Emite como Server-Sent Events los eventos de la zona que cumplen los filtros (los mismos que [Listar eventos](https://developers.openwaf.dev/es/api/security-events/#list-events); no se usan `since`, `until` ni `limit`), en el orden en que los recibe OpenWAF. La autenticación, la zona y los filtros se comprueban antes de empezar, así que los errores son respuestas JSON.

El stream empieza tras `Last-Event-ID` (lo envía `EventSource` al reconectar) o `cursor`; sin ellos, 10 segundos antes de la petición. Carga antes `GET …/events` y descarta los `event_id` repetidos. Tramas:

- `event: ready` primero, con `{"poll_ms": 2000, "heartbeat_ms": 15000, "max_age_s": 600}`.
- `event: event` por cada evento, con su cursor en `id` y el evento en `data` (como en Listar eventos).
- `event: ping` tras `heartbeat_ms` sin tráfico, para detectar conexiones colgadas.
- `event: bye` tras `max_age_s`, y el stream se cierra; `EventSource` reconecta con `Last-Event-ID` sin perder eventos.
- `event: error` con `{"code": "unavailable"}` si falla el almacén de eventos, y el stream se cierra.

Un evento recibido más de una hora después de producirse no aparece en el stream (sí en Listar eventos). Como mucho 10 streams por organización.

**Rol necesario:** `readonly` o superior.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |
| `zone` | ruta | string (uuid) | Sí | Id de la zona (consulta [Listar zonas](https://developers.openwaf.dev/es/api/zones/#list-zones)). |
| `Last-Event-ID` | cabecera | string | No | `id` del último evento recibido de un stream (lo envía EventSource al reconectar); tiene prioridad sobre `cursor`. |
| `cursor` | consulta | string | No | `id` de un evento de un stream anterior: se empieza justo después. |
| `action` | consulta | string | No | Repetible (cualquiera de los valores, como mucho 20). Uno de `block`, `log`, `challenge`, `allow`. |
| `rule_id` | consulta | integer | No | Repetible. De 0 a 4294967295. |
| `ip` | consulta | string | No | IPv4 o IPv6 (se normaliza); repetible. |
| `country` | consulta | string | No | ISO 3166-1 alfa-2 (se admite en minúsculas); repetible. Patrón `^[A-Za-z]{2}$`. |
| `host` | consulta | string | No | Nombre exacto (sin distinguir mayúsculas); repetible. De 1 a 253 caracteres. |
| `path` | consulta | string | No | Ruta exacta (1-2048 bytes); repetible. De 1 a 2048 caracteres. |
| `status` | consulta | integer | No | Estado HTTP de la respuesta; repetible. De 100 a 599. |
| `rate_rule` | consulta | string | No | Eventos de esa regla de límite de peticiones (rule_name `rate_rule:<id>`, attack_type `rate_limit`); repetible. Patrón `^[a-z0-9][a-z0-9-]{0,39}$`. |
| `rule_name` | consulta | string | No | rule_name exacto (el código de un bloqueo de egress, p. ej. `injection_detected`); repetible. De 1 a 200 caracteres. |
| `not_action` | consulta | string | No | Excluye esas acciones; repetible. Uno de `block`, `log`, `challenge`, `allow`. |
| `not_rule_id` | consulta | integer | No | Excluye esas reglas; repetible. De 0 a 4294967295. |
| `not_ip` | consulta | string | No | Excluye esas IP; repetible. |
| `not_country` | consulta | string | No | Excluye esos países; repetible. Patrón `^[A-Za-z]{2}$`. |
| `not_host` | consulta | string | No | Excluye esos nombres; repetible. De 1 a 253 caracteres. |
| `not_path` | consulta | string | No | Excluye esas rutas exactas; repetible. De 1 a 2048 caracteres. |
| `not_status` | consulta | integer | No | Excluye esos estados HTTP; repetible. De 100 a 599. |
| `not_rate_rule` | consulta | string | No | Excluye los eventos de esas reglas de límite de peticiones; repetible. Patrón `^[a-z0-9][a-z0-9-]{0,39}$`. |
| `not_rule_name` | consulta | string | No | Excluye esos rule_name; repetible. De 1 a 200 caracteres. |
| `q` | consulta | string | No | Texto libre (sin distinguir mayúsculas) en host, ruta, IP, método, rule_name, attack_type, msg, user agent, país, rule_id y status. Como mucho 200 caracteres. |

#### Respuesta

- `200`: Stream de Server-Sent Events (tramas descritas arriba). Cuerpo de tipo `text/event-stream`.

#### Errores

| Estado | Descripción |
|---|---|
| `400` | Petición mal formada (`bad_request`). |
| `401` | Token ausente, no válido o caducado (`unauthorized`). |
| `404` | No existe o el token no lo ve (`not_found`). |
| `429` | `too_many_streams`: demasiados streams abiertos en la organización; `Retry-After` en segundos. |
| `503` | No disponible ahora (`unavailable`); reinténtalo más tarde. |

#### Ejemplo

No está en los SDK: abre el stream con `EventSource` o con cualquier cliente de Server-Sent Events.

```sh
curl -N "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/events/stream?action=block" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

```text
retry: 3000
event: ready
data: {"poll_ms":2000,"heartbeat_ms":15000,"max_age_s":600}

id: c2V0MTcz…
event: event
data: {"event_id":"…","action":"block","host":"www.example.com","path":"/wp-login.php","client_ip":"203.0.113.10"}
```
