Security events are the requests the edge blocked, challenged or logged (WAF, rules, rate limits, shields). All endpoints share the same filters; every filter can be repeated and different filters combine with AND.
| Operation | Endpoint |
|---|---|
| List events | GET /v1/orgs/{org}/zones/{zone}/events |
| Summarize events | GET /v1/orgs/{org}/zones/{zone}/events/summary |
| Events histogram | GET /v1/orgs/{org}/zones/{zone}/events/histogram |
| Stream events | GET /v1/orgs/{org}/zones/{zone}/events/stream |
List events
GET /v1/orgs/{org}/zones/{zone}/eventsLists the security events of the zone, newest first. Every filter can be repeated (action=block&action=log matches any of the values) with at most 20 values; different filters combine with AND, and the not_… filters exclude events with any of their values. q searches text (case-insensitive) in host, path, IP, method, rule, attack type, message, user agent, country, rule id and status. Page with cursor set to next_cursor.
Required role: readonly or higher.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
org | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise 404). |
zone | path | string (uuid) | Yes | Zone id (see List zones). |
since | query | string (date-time) | No | RFC 3339; until minus 24 hours by default. |
until | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
action | query | string | No | Repeatable (any of the values, at most 20). One of block, log, challenge, allow. |
rule_id | query | integer | No | Repeatable. From 0 to 4294967295. |
ip | query | string | No | IPv4 or IPv6 (normalized); repeatable. |
country | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted); repeatable. Pattern ^[A-Za-z]{2}$. |
host | query | string | No | Exact name (case-insensitive); repeatable. 1 to 253 characters. |
path | query | string | No | Exact path (1-2048 bytes); repeatable. 1 to 2048 characters. |
status | query | integer | No | HTTP status of the response; repeatable. From 100 to 599. |
rate_rule | query | string | No | Events of that rate limiting rule (rule_name rate_rule:<id>, attack_type rate_limit); repeatable. Pattern ^[a-z0-9][a-z0-9-]{0,39}$. |
rule_name | query | string | No | Exact rule_name (the code of an egress block, e.g. injection_detected); repeatable. 1 to 200 characters. |
not_action | query | string | No | Excludes those actions; repeatable. One of block, log, challenge, allow. |
not_rule_id | query | integer | No | Excludes those rules; repeatable. From 0 to 4294967295. |
not_ip | query | string | No | Excludes those IPs; repeatable. |
not_country | query | string | No | Excludes those countries; repeatable. Pattern ^[A-Za-z]{2}$. |
not_host | query | string | No | Excludes those names; repeatable. 1 to 253 characters. |
not_path | query | string | No | Excludes those exact paths; repeatable. 1 to 2048 characters. |
not_status | query | integer | No | Excludes those HTTP statuses; repeatable. From 100 to 599. |
not_rate_rule | query | string | No | Excludes the events of those rate limiting rules; repeatable. Pattern ^[a-z0-9][a-z0-9-]{0,39}$. |
not_rule_name | query | string | No | Excludes those rule_name values; repeatable. 1 to 200 characters. |
q | query | string | No | Free text (case-insensitive) in host, path, IP, method, rule_name, attack_type, msg, user agent, country, rule_id and status. At most 200 characters. |
limit | query | integer | No | Maximum number of items. Default 100. From 1 to 1000. |
cursor | query | string | No | next_cursor of the previous page. |
Response
200: Returns an object with the fields below.
| Field | Type | Description |
|---|---|---|
items | array of EdgeEvent | |
next_cursor | string |
Errors
| Status | Description |
|---|---|
400 | Malformed request (bad_request). |
404 | Not found, or not visible to the token (not_found). |
503 | Not available right now (unavailable); retry later. |
Example
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"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.listEvents({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
action: "block",
limit: 50
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token from OPENWAF_TOKEN
result = owf.list_events(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d", action="block", limit=50){
"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": ""
}Summarize events
GET /v1/orgs/{org}/zones/{zone}/events/summaryCounts the events that match the filters (the same as List events) by action, with the 10 rules, the 10 countries and the 10 rate limiting rules with most events.
Required role: readonly or higher.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
org | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise 404). |
zone | path | string (uuid) | Yes | Zone id (see List zones). |
since | query | string (date-time) | No | RFC 3339; until minus 24 hours by default. |
until | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
action | query | string | No | Repeatable (any of the values, at most 20). One of block, log, challenge, allow. |
rule_id | query | integer | No | Repeatable. From 0 to 4294967295. |
ip | query | string | No | IPv4 or IPv6 (normalized); repeatable. |
country | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted); repeatable. Pattern ^[A-Za-z]{2}$. |
host | query | string | No | Exact name (case-insensitive); repeatable. 1 to 253 characters. |
path | query | string | No | Exact path (1-2048 bytes); repeatable. 1 to 2048 characters. |
status | query | integer | No | HTTP status of the response; repeatable. From 100 to 599. |
rate_rule | query | string | No | Events of that rate limiting rule (rule_name rate_rule:<id>, attack_type rate_limit); repeatable. Pattern ^[a-z0-9][a-z0-9-]{0,39}$. |
rule_name | query | string | No | Exact rule_name (the code of an egress block, e.g. injection_detected); repeatable. 1 to 200 characters. |
not_action | query | string | No | Excludes those actions; repeatable. One of block, log, challenge, allow. |
not_rule_id | query | integer | No | Excludes those rules; repeatable. From 0 to 4294967295. |
not_ip | query | string | No | Excludes those IPs; repeatable. |
not_country | query | string | No | Excludes those countries; repeatable. Pattern ^[A-Za-z]{2}$. |
not_host | query | string | No | Excludes those names; repeatable. 1 to 253 characters. |
not_path | query | string | No | Excludes those exact paths; repeatable. 1 to 2048 characters. |
not_status | query | integer | No | Excludes those HTTP statuses; repeatable. From 100 to 599. |
not_rate_rule | query | string | No | Excludes the events of those rate limiting rules; repeatable. Pattern ^[a-z0-9][a-z0-9-]{0,39}$. |
not_rule_name | query | string | No | Excludes those rule_name values; repeatable. 1 to 200 characters. |
q | query | string | No | Free text (case-insensitive) in host, path, IP, method, rule_name, attack_type, msg, user agent, country, rule_id and status. At most 200 characters. |
Response
200: Returns an object with the fields below.
| Field | Type | Description |
|---|---|---|
since | string (date-time) | |
until | string (date-time) | |
total | integer | |
by_action | array of KeyCount | |
top_rules | array of object | |
top_rules[].rule_id | integer | Id of the rule. |
top_rules[].rule_name | string | Name or code of the rule. |
top_rules[].count | integer | |
top_countries | array of KeyCount | |
top_rate_rules | array of KeyCount | Events of rate limiting rules per rule id (key). |
Errors
| Status | Description |
|---|---|
400 | Malformed request (bad_request). |
404 | Not found, or not visible to the token (not_found). |
503 | Not available right now (unavailable); retry later. |
Example
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/events/summary" \
-H "Authorization: Bearer $OPENWAF_TOKEN"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getEventsSummary({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token from OPENWAF_TOKEN
result = owf.get_events_summary(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"){
"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
}
]
}Events histogram
GET /v1/orgs/{org}/zones/{zone}/events/histogramCounts the events that match the filters per time interval and action. Bars of step seconds are aligned to multiples of step since the Unix epoch, with no gaps (empty bars count 0); the first one contains since and the last one starts before until.
Required role: readonly or higher.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
org | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise 404). |
zone | path | string (uuid) | Yes | Zone id (see List zones). |
since | query | string (date-time) | No | RFC 3339; until minus 24 hours by default. |
until | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
step | query | integer | No | Seconds per bar (1-86400). By default the smallest of 1, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 10800, 21600, 43200 and 86400 that gives at most 60 bars; with an explicit step, more than 1000 bars answers 400. From 1 to 86400. |
action | query | string | No | Repeatable (any of the values, at most 20). One of block, log, challenge, allow. |
rule_id | query | integer | No | Repeatable. From 0 to 4294967295. |
ip | query | string | No | IPv4 or IPv6 (normalized); repeatable. |
country | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted); repeatable. Pattern ^[A-Za-z]{2}$. |
host | query | string | No | Exact name (case-insensitive); repeatable. 1 to 253 characters. |
path | query | string | No | Exact path (1-2048 bytes); repeatable. 1 to 2048 characters. |
status | query | integer | No | HTTP status of the response; repeatable. From 100 to 599. |
rate_rule | query | string | No | Events of that rate limiting rule (rule_name rate_rule:<id>, attack_type rate_limit); repeatable. Pattern ^[a-z0-9][a-z0-9-]{0,39}$. |
rule_name | query | string | No | Exact rule_name (the code of an egress block, e.g. injection_detected); repeatable. 1 to 200 characters. |
not_action | query | string | No | Excludes those actions; repeatable. One of block, log, challenge, allow. |
not_rule_id | query | integer | No | Excludes those rules; repeatable. From 0 to 4294967295. |
not_ip | query | string | No | Excludes those IPs; repeatable. |
not_country | query | string | No | Excludes those countries; repeatable. Pattern ^[A-Za-z]{2}$. |
not_host | query | string | No | Excludes those names; repeatable. 1 to 253 characters. |
not_path | query | string | No | Excludes those exact paths; repeatable. 1 to 2048 characters. |
not_status | query | integer | No | Excludes those HTTP statuses; repeatable. From 100 to 599. |
not_rate_rule | query | string | No | Excludes the events of those rate limiting rules; repeatable. Pattern ^[a-z0-9][a-z0-9-]{0,39}$. |
not_rule_name | query | string | No | Excludes those rule_name values; repeatable. 1 to 200 characters. |
q | query | string | No | Free text (case-insensitive) in host, path, IP, method, rule_name, attack_type, msg, user agent, country, rule_id and status. At most 200 characters. |
Response
200: Returns EventsHistogram.
| Field | Type | Description |
|---|---|---|
since | string (date-time) | |
until | string (date-time) | |
step | integer | Seconds per bar. |
total | integer | Events in [since, until). |
buckets | array of object | |
buckets[].ts | string (date-time) | Start of the bar. |
buckets[].count | integer | |
buckets[].block | integer | |
buckets[].log | integer | |
buckets[].challenge | integer | |
buckets[].allow | integer |
Errors
| Status | Description |
|---|---|
400 | Malformed request (bad_request). |
404 | Not found, or not visible to the token (not_found). |
503 | Not available right now (unavailable); retry later. |
Example
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/events/histogram" \
-H "Authorization: Bearer $OPENWAF_TOKEN"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getEventsHistogram({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token from OPENWAF_TOKEN
result = owf.get_events_histogram(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"){
"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 events
GET /v1/orgs/{org}/zones/{zone}/events/streamStreams the zone's events that match the filters (the same as List events; since, until and limit are not used) as Server-Sent Events, in the order OpenWAF receives them. Authentication, zone and filters are checked before the stream starts, so errors are JSON answers.
The stream starts after Last-Event-ID (sent by EventSource when it reconnects) or cursor; without them, 10 seconds before the request. Load GET …/events first and drop repeated event_id values. Frames:
event: readyfirst, with{"poll_ms": 2000, "heartbeat_ms": 15000, "max_age_s": 600}.event: eventfor each event, with its cursor inidand the event indata(as in List events).event: pingafterheartbeat_mswithout traffic, to detect hung connections.event: byeaftermax_age_s, then the stream closes;EventSourcereconnects withLast-Event-IDwithout losing events.event: errorwith{"code": "unavailable"}if the event store fails, then the stream closes.
An event received more than an hour after it happened is not streamed (it is listed by List events). At most 10 streams per organization.
Required role: readonly or higher.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
org | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise 404). |
zone | path | string (uuid) | Yes | Zone id (see List zones). |
Last-Event-ID | header | string | No | id of the last event received from a stream (sent by EventSource when it reconnects); takes precedence over cursor. |
cursor | query | string | No | id of an event of a previous stream: starts right after it. |
action | query | string | No | Repeatable (any of the values, at most 20). One of block, log, challenge, allow. |
rule_id | query | integer | No | Repeatable. From 0 to 4294967295. |
ip | query | string | No | IPv4 or IPv6 (normalized); repeatable. |
country | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted); repeatable. Pattern ^[A-Za-z]{2}$. |
host | query | string | No | Exact name (case-insensitive); repeatable. 1 to 253 characters. |
path | query | string | No | Exact path (1-2048 bytes); repeatable. 1 to 2048 characters. |
status | query | integer | No | HTTP status of the response; repeatable. From 100 to 599. |
rate_rule | query | string | No | Events of that rate limiting rule (rule_name rate_rule:<id>, attack_type rate_limit); repeatable. Pattern ^[a-z0-9][a-z0-9-]{0,39}$. |
rule_name | query | string | No | Exact rule_name (the code of an egress block, e.g. injection_detected); repeatable. 1 to 200 characters. |
not_action | query | string | No | Excludes those actions; repeatable. One of block, log, challenge, allow. |
not_rule_id | query | integer | No | Excludes those rules; repeatable. From 0 to 4294967295. |
not_ip | query | string | No | Excludes those IPs; repeatable. |
not_country | query | string | No | Excludes those countries; repeatable. Pattern ^[A-Za-z]{2}$. |
not_host | query | string | No | Excludes those names; repeatable. 1 to 253 characters. |
not_path | query | string | No | Excludes those exact paths; repeatable. 1 to 2048 characters. |
not_status | query | integer | No | Excludes those HTTP statuses; repeatable. From 100 to 599. |
not_rate_rule | query | string | No | Excludes the events of those rate limiting rules; repeatable. Pattern ^[a-z0-9][a-z0-9-]{0,39}$. |
not_rule_name | query | string | No | Excludes those rule_name values; repeatable. 1 to 200 characters. |
q | query | string | No | Free text (case-insensitive) in host, path, IP, method, rule_name, attack_type, msg, user agent, country, rule_id and status. At most 200 characters. |
Response
200: Server-Sent Events stream (frames described above). Body of typetext/event-stream.
Errors
| Status | Description |
|---|---|
400 | Malformed request (bad_request). |
401 | Missing, invalid or expired token (unauthorized). |
404 | Not found, or not visible to the token (not_found). |
429 | too_many_streams: too many open streams in the organization; Retry-After in seconds. |
503 | Not available right now (unavailable); retry later. |
Example
Not in the SDKs: open the stream with EventSource or any Server-Sent Events client.
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"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"}