Edge rules of a zone, applied by the edge to every proxied name or only to the names listed in hosts. Four kinds exist, ip, geo, waf_exception and rate_limit. See WAF and Rules.
| Operation | Endpoint |
|---|---|
| List rules | GET /v1/orgs/{org}/zones/{zone}/rules |
| Create rule | POST /v1/orgs/{org}/zones/{zone}/rules |
| Delete rule | DELETE /v1/orgs/{org}/zones/{zone}/rules/{rule} |
List rules
GET /v1/orgs/{org}/zones/{zone}/rulesLists the edge rules of the zone with their ids and the names each one is limited to (hosts; empty means every name).
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). |
Response
200: Returnsitems, an array of ZoneRule.
| Field | Type | Description |
|---|---|---|
items[].kind | string | One of ip, geo, waf_exception, rate_limit. |
items[].ip | string | IP or CIDR. |
items[].action | string | One of block, allow. |
items[].country_code | string | ISO 3166-1 alpha-2 country code. Pattern ^[A-Za-z]{2}$. |
items[].rule_id | integer | Id of the rule. Minimum 0. |
items[].path_pattern | string | "" or * = all; ending in * = prefix; otherwise exact. |
items[].method | string | |
items[].rpm | integer | Minimum 0. |
items[].burst | integer | Default 0. Minimum 0. |
items[].note | string | At most 500 characters. |
items[].hosts | array of string | Names it applies to; absent = every name of the zone. |
items[].id | integer | Id. |
items[].zone_id | string (uuid) | Zone id. |
items[].created_at | string (date-time) | Creation time. |
Example
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/rules" \
-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.listRules({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token from OPENWAF_TOKEN
result = owf.list_rules(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"){
"items": [
{
"id": 3,
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"kind": "ip",
"ip": "203.0.113.0/24",
"action": "block",
"note": "Abusive scanner",
"hosts": [],
"created_at": "2026-10-27T09:30:00Z"
}
]
}Create rule
POST /v1/orgs/{org}/zones/{zone}/rulesAdds an edge rule. The fields depend on kind:
kind | Fields |
|---|---|
ip | ip (address or CIDR), action |
geo | country_code, action |
waf_exception | rule_id (0 = every WAF rule), path_pattern |
rate_limit | path_pattern, method, rpm, burst |
rate_limit rules are the advanced, per-path rate limits; the basic per-IP limit is rate_limit_rpm in the edge settings. hosts limits the rule to those names (the zone or below it); a name without a proxied record gives a warning.
Required role: tech or higher.
Change controls: dry_run · If-Match (optional) · X-Change-Reason (required)
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). |
X-Change-Reason | header | string | Yes | Reason of the change: 1-500 UTF-8 characters after trimming spaces, no control characters. If it is missing or invalid: 422 reason_required. 1 to 500 characters. |
If-Match | header | string | No | Expected configuration version, e.g. "12" (W/ accepted). If it is not the current one: 412 version_conflict. |
dry_run | query | string | No | 1/true simulates the change: answers 200 with the plan (Applied) and saves nothing. 0/false accepted; any other value: 400. One of 1, true, 0, false. |
Request body
Content type application/json. Schema: RuleInput.
| Field | Type | Required | Description |
|---|---|---|---|
kind | string | Yes | One of ip, geo, waf_exception, rate_limit. |
ip | string | No | IP or CIDR. |
action | string | No | One of block, allow. |
country_code | string | No | ISO 3166-1 alpha-2 country code. Pattern ^[A-Za-z]{2}$. |
rule_id | integer | No | Id of the rule. Minimum 0. |
path_pattern | string | No | "" or * = all; ending in * = prefix; otherwise exact. |
method | string | No | |
rpm | integer | No | Minimum 0. |
burst | integer | No | Default 0. Minimum 0. |
note | string | No | At most 500 characters. |
hosts | array of string | No | Names of the zone (the zone or below it) it applies to; absent or empty = all. |
Response
200: Withdry_run=1, the plan of the change (Applied); nothing is saved.201: Created. Returns ZoneRule.
| Field | Type | Description |
|---|---|---|
kind | string | One of ip, geo, waf_exception, rate_limit. |
ip | string | IP or CIDR. |
action | string | One of block, allow. |
country_code | string | ISO 3166-1 alpha-2 country code. Pattern ^[A-Za-z]{2}$. |
rule_id | integer | Id of the rule. Minimum 0. |
path_pattern | string | "" or * = all; ending in * = prefix; otherwise exact. |
method | string | |
rpm | integer | Minimum 0. |
burst | integer | Default 0. Minimum 0. |
note | string | At most 500 characters. |
hosts | array of string | Names it applies to; absent = every name of the zone. |
id | integer | Id. |
zone_id | string (uuid) | Zone id. |
created_at | string (date-time) | Creation time. |
Response headers: ETag, X-Config-Version.
Errors
| Status | Description |
|---|---|
400 | Malformed request (bad_request). |
409 | zone_merged: the zone is merged into another one. |
412 | version_conflict: If-Match does not match the current version. |
422 | Validation error (invalid) or missing or invalid reason (reason_required). |
Example
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/rules" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: Block an abusive network" \
-H 'If-Match: "12"' \
-H "Content-Type: application/json" \
--data '{
"kind": "ip",
"ip": "203.0.113.0/24",
"action": "block",
"note": "Abusive scanner"
}'import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.createRule({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
body: {
kind: "ip",
ip: "203.0.113.0/24",
action: "block",
note: "Abusive scanner"
}
}, {
reason: "Block an abusive network",
ifMatch: 12
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token from OPENWAF_TOKEN
result = owf.create_rule(
zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
body={
"kind": "ip",
"ip": "203.0.113.0/24",
"action": "block",
"note": "Abusive scanner"
},
reason="Block an abusive network",
if_match=12,
){
"id": 3,
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"kind": "ip",
"ip": "203.0.113.0/24",
"action": "block",
"note": "Abusive scanner",
"hosts": [],
"created_at": "2026-10-27T09:30:00Z"
}Delete rule
DELETE /v1/orgs/{org}/zones/{zone}/rules/{rule}Removes an edge rule of the zone.
Required role: tech or higher.
Change controls: dry_run · If-Match (optional) · X-Change-Reason (required)
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). |
rule | path | integer | Yes | Rule id. |
X-Change-Reason | header | string | Yes | Reason of the change: 1-500 UTF-8 characters after trimming spaces, no control characters. If it is missing or invalid: 422 reason_required. 1 to 500 characters. |
If-Match | header | string | No | Expected configuration version, e.g. "12" (W/ accepted). If it is not the current one: 412 version_conflict. |
dry_run | query | string | No | 1/true simulates the change: answers 200 with the plan (Applied) and saves nothing. 0/false accepted; any other value: 400. One of 1, true, 0, false. |
Response
200: Withdry_run=1, the plan of the change (Applied); nothing is saved.204: Deleted. No body.
Response headers: ETag, X-Config-Version.
Errors
| Status | Description |
|---|---|
400 | Malformed request (bad_request). |
404 | Not found, or not visible to the token (not_found). |
409 | zone_merged: the zone is merged into another one. |
412 | version_conflict: If-Match does not match the current version. |
422 | Validation error (invalid) or missing or invalid reason (reason_required). |
Example
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/rules/3" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: The network is no longer abusive" \
-H 'If-Match: "12"'import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.deleteRule({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
rule: 3
}, {
reason: "The network is no longer abusive",
ifMatch: 12
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token from OPENWAF_TOKEN
result = owf.delete_rule(
zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
rule=3,
reason="The network is no longer abusive",
if_match=12,
)