The OpenWAF API is a JSON REST API over HTTPS. It manages everything the dashboard does: zones and DNS records, edge settings and rules, certificates and cache, analytics, AI agents, egress, members and API tokens. The SDKs, the CLI and the MCP server use the same API.
Base URL
https://dash.openwaf.dev/v1Organization resources live under /v1/orgs/{org} and zone resources under /v1/orgs/{org}/zones/{zone}. {org} is the id of your organization and {zone} the id of a zone, both UUIDs. GET /v1/orgs returns the organization of the token, and List zones returns the zone ids.
Authentication
Every request carries an API token in the Authorization header:
GET /v1/orgs HTTP/1.1
Host: dash.openwaf.dev
Authorization: Bearer owf_…Create tokens in the dashboard (API tokens) or with Create token. A token acts with a role and can be limited to one zone or one client. A request for an organization other than the token's answers 404, exactly like a resource that does not exist. See Authentication.
Requests and responses
- Request bodies are JSON (
Content-Type: application/json) unless an operation says otherwise, up to 1 MiB. Unknown fields are rejected with400 bad_request. - Times are RFC 3339 in UTC and ids are UUIDs.
- Lists answer
{"items": […]}. Long lists page withlimitand a cursor (cursor,before) described in each operation. 204 No Contentanswers have no body.
Configuration changes
The configuration of a zone (records, edge settings, rules, its client) is versioned: every change creates a new version with its author and reason, visible in Configuration versions and in the audit log. Writes that change configuration accept three controls:
| Control | Where | Effect |
|---|---|---|
X-Change-Reason | Header | Reason of the change, 1-500 characters, recorded in the history and the audit log. Required by most writes: without it they answer 422 reason_required. |
dry_run=1 | Query | Simulates the change: answers 200 with the plan (Applied), the diff and the version it would create. Nothing is saved. |
If-Match | Header | Expected current version, e.g. "12". If the zone has changed since, the write answers 412 version_conflict with current_version. |
Successful writes answer the new version in the X-Config-Version header and in ETag (quoted, ready for If-Match). The safe way to change a zone, the same two steps the MCP server enforces:
- Send the write with
dry_run=1and check the plan; read the current version fromfrom_version(or the zone'sETag). - Send the same write without
dry_run, withIf-Match: "<version>"and anX-Change-Reason. If someone changed the zone in between you get412 version_conflict: simulate again.
Errors
Errors answer an HTTP status and a JSON body with a stable code:
{"error": {"code": "invalid", "message": "ttl must be between 60 and 86400", "field": "ttl"}}See Errors for every code.
Rate limits
Some operations have their own limits and answer 429 with a Retry-After header (seconds) when they are exceeded:
| Operation | Limit |
|---|---|
| Purge cache | Per organization: 10 purges per minute with bursts of 30 (everything, hosts, prefixes, tags) and 500 URLs per minute. |
| Scan agent readiness | Once per name per minute. |
| Security events stream and egress events stream | 10 open streams per organization. |
Versioning
Every endpoint is under /v1. Responses may gain new fields and enums new values over time: clients should ignore fields they do not know. Changes to the API are announced in the changelog.
SDKs, CLI and MCP
openwaf-cloud ctl, the API from the terminal and from scripts.MCP serverthe API as tools for AI assistants, at https://mcp.openwaf.dev/mcp.The SDK files are also served by the API without authentication: https://dash.openwaf.dev/v1/sdk/openwaf.ts and https://dash.openwaf.dev/v1/sdk/openwaf.py.
Reference
- Schemas: every object returned or accepted by the API.
Dashboard session
Sign-in (/v1/auth/…), invitation acceptance and account deletion serve the dashboard with a session cookie and are not part of the token API. With a session cookie, the dashboard also calls the routes of this reference; writes then require the X-CSRF-Token header. Integrations always use an API token.