Una zona es un dominio dado de alta en OpenWAF. En modo delegated OpenWAF sirve su DNS cuando el registrador la delega en los servidores de nombres de la zona; en modo external el DNS se queda donde está y cada nombre con proxy apunta al borde con un CNAME a edge.openwaf.dev. Una zona sigue pending hasta que se verifica su delegación o, en una zona externa, el TXT que prueba su propiedad.
| Operación | Ruta |
|---|---|
| Listar zonas | GET /v1/orgs/{org}/zones |
| Crear una zona | POST /v1/orgs/{org}/zones |
| Crear zonas en bloque | POST /v1/orgs/{org}/zones/bulk |
| Ver una zona | GET /v1/orgs/{org}/zones/{zone} |
| Borrar una zona | DELETE /v1/orgs/{org}/zones/{zone} |
| Ver el estado de la delegación | GET /v1/orgs/{org}/zones/{zone}/delegation |
| Comprobar la delegación ahora | POST /v1/orgs/{org}/zones/{zone}/delegation/check |
| Comprobar los nombres de una zona externa | GET /v1/orgs/{org}/zones/{zone}/hosts |
| Fusionar una zona en su padre | POST /v1/orgs/{org}/zones/{zone}/merge |
| Deshacer la fusión de una zona | POST /v1/orgs/{org}/zones/{zone}/unmerge |
Listar zonas
GET /v1/orgs/{org}/zonesLista las zonas de la organización. Las zonas fusionadas en otra se ocultan salvo con include=merged.
Rol necesario: readonly o superior. Un token limitado a una zona solo ve esa zona; uno limitado a un cliente solo ve las zonas de su cliente.
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). |
include | consulta | string | No | merged lista también las zonas fusionadas en otra (ocultas por defecto). Uno de merged. |
Respuesta
200: Devuelveitems, un array de Zone.
| Campo | Tipo | Descripción |
|---|---|---|
items[].id | string (uuid) | Id. |
items[].org_id | string (uuid) | Id de la organización. |
items[].name | string | Nombre. |
items[].status | string | Uno de pending, active, moved, disabled. |
items[].dns_mode | string | delegated: NS de OpenWAF. external: el cliente conserva su DNS y apunta sus sitios al borde (CNAME/A). Uno de delegated, external. Por defecto delegated. |
items[].ns_pair | array de string | Servidores de nombres que usan las zonas delegadas. Exactamente 2 elementos. |
items[].delegation_checked_at | string (date-time) | Última comprobación de la delegación. |
items[].delegation | CheckResult o VerifyResult | Última comprobación: CheckResult (delegada) o VerifyResult (externa). |
items[].activated_at | string (date-time) | Cuándo pasó a activa. |
items[].moved_at | string (date-time) | Cuándo dejó de estar delegada en OpenWAF. |
items[].disabled_at | string (date-time) | Cuándo se desactivó. |
items[].verification | ZoneVerification | |
items[].cname_target | string | Solo zonas externas: destino del CNAME de cada sitio. |
items[].edge_addresses | EdgeAddresses | |
items[].config_version | integer | Versión de configuración actual (también en ETag). |
items[].proxied_names | integer | Nombres con registro con proxy. |
items[].merged_into | string (uuid) | Solo zonas fusionadas (status disabled): zona padre. |
items[].merged_at | string (date-time) | Solo zonas fusionadas; se purgan a los 14 días. |
items[].created_at | string (date-time) | Fecha de creación. |
items[].updated_at | string (date-time) | Último cambio. |
items[].client_id | string (uuid) o null | Cliente de la organización al que pertenece la zona; null = ninguno. |
Ejemplo
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones" \
-H "Authorization: Bearer $OPENWAF_TOKEN"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.listZones();from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.list_zones(){
"items": [
{
"id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
"name": "example.com",
"status": "active",
"dns_mode": "delegated",
"ns_pair": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"delegation_checked_at": "2026-10-27T09:00:00Z",
"activated_at": "2026-03-02T11:40:00Z",
"config_version": 12,
"proxied_names": 3,
"client_id": null,
"created_at": "2026-03-02T10:20:00Z",
"updated_at": "2026-10-27T09:00:00Z"
}
]
}Crear una zona
POST /v1/orgs/{org}/zonesDa de alta una zona y crea la versión 1 de su configuración (el motivo por defecto es el alta de la zona).
delegated(por defecto): OpenWAF sirve el DNS de la zona. La zona siguependinghasta que el registrador la delega en sus servidores de nombres (ns_pair). Por defecto se importan enseguida los registros encontrados en el DNS público (import).external: el DNS se queda en su proveedor actual. Apunta cada nombre con proxy al borde con un CNAME acname_target(o registros A/AAAA aedge_addressesen el ápice) y publica el TXT deverificationpara probar la propiedad; una vez verificada, la zona pasa aactive. Las zonas externas solo admiten registros A/AAAA/CNAME con proxy.
Se rechaza un dominio que sea padre o subdominio de una zona de otra organización (o, con una credencial limitada a un cliente, de otro cliente). Una zona que sigue pending demasiado tiempo se borra sola.
Rol necesario: tech o superior. No disponible para tokens limitados a una zona.
Controles de cambio: dry_run · If-Match (opcional) · X-Change-Reason (opcional)
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). |
X-Change-Reason | cabecera | string | No | Motivo del cambio (1-500 caracteres UTF-8, sin caracteres de control); por defecto, el alta de la zona. De 1 a 500 caracteres. |
If-Match | cabecera | string | No | Versión de configuración esperada, p. ej. "12" (se admite W/). Si no es la actual: 412 version_conflict. |
dry_run | consulta | string | No | 1/true simula el cambio: responde 200 con el plan (Applied) y no guarda nada. Admite 0/false; otro valor: 400. Uno de 1, true, 0, false. |
Cuerpo de la petición
Tipo de contenido application/json.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
name | string | Sí | Nombre. |
dns_mode | string | No | delegated: NS de OpenWAF. external: el cliente conserva su DNS y apunta sus sitios al borde (CNAME/A). Uno de delegated, external. Por defecto delegated. |
client_id | string (uuid) | No | Asigna la zona a un cliente. Con una credencial limitada a un cliente, la zona es siempre de ese cliente. |
import | boolean | No | Zonas delegadas: crea justo después del alta los registros encontrados en el DNS público. Por defecto true. |
Respuesta
200: Condry_run=1, el plan del cambio (Applied); no se guarda nada.201: Creada. Devuelve CreatedZone.
| Campo | Tipo | Descripción |
|---|---|---|
id | string (uuid) | Id. |
org_id | string (uuid) | Id de la organización. |
name | string | Nombre. |
status | string | Uno de pending, active, moved, disabled. |
dns_mode | string | delegated: NS de OpenWAF. external: el cliente conserva su DNS y apunta sus sitios al borde (CNAME/A). Uno de delegated, external. Por defecto delegated. |
ns_pair | array de string | Servidores de nombres que usan las zonas delegadas. Exactamente 2 elementos. |
delegation_checked_at | string (date-time) | Última comprobación de la delegación. |
delegation | CheckResult o VerifyResult | Última comprobación: CheckResult (delegada) o VerifyResult (externa). |
activated_at | string (date-time) | Cuándo pasó a activa. |
moved_at | string (date-time) | Cuándo dejó de estar delegada en OpenWAF. |
disabled_at | string (date-time) | Cuándo se desactivó. |
verification | ZoneVerification | |
cname_target | string | Solo zonas externas: destino del CNAME de cada sitio. |
edge_addresses | EdgeAddresses | |
config_version | integer | Versión de configuración actual (también en ETag). |
proxied_names | integer | Nombres con registro con proxy. |
merged_into | string (uuid) | Solo zonas fusionadas (status disabled): zona padre. |
merged_at | string (date-time) | Solo zonas fusionadas; se purgan a los 14 días. |
created_at | string (date-time) | Fecha de creación. |
updated_at | string (date-time) | Último cambio. |
client_id | string (uuid) o null | Cliente de la organización al que pertenece la zona; null = ninguno. |
detected_records | integer | Registros encontrados en el DNS público. |
imported_records | integer | Registros creados en la zona. |
import_warnings | array de string | Avisos de la importación. |
Cabeceras de la respuesta: ETag, X-Config-Version.
Errores
| Estado | Descripción |
|---|---|
400 | Petición mal formada (bad_request). |
403 | Sin permiso (forbidden) o límite de zonas de la organización alcanzado (zone_limit). |
409 | El dominio ya está dado de alta (zone_exists) o es padre o subdominio de una zona de otra organización o de otro cliente (zone_overlaps). |
412 | version_conflict: If-Match no coincide con la versión actual. |
422 | Error de validación (invalid) o motivo ausente/no válido (reason_required). |
503 | Las zonas delegadas no están disponibles ahora (unavailable): solo se admiten zonas external. |
Ejemplo
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: Add example.com" \
-H "Content-Type: application/json" \
--data '{
"name": "example.com",
"dns_mode": "delegated"
}'import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.createZone({
body: {
name: "example.com",
dns_mode: "delegated"
}
}, {
reason: "Add example.com"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.create_zone(
body={
"name": "example.com",
"dns_mode": "delegated"
},
reason="Add example.com",
){
"id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
"name": "example.com",
"status": "pending",
"dns_mode": "delegated",
"ns_pair": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"delegation_checked_at": "2026-10-27T09:00:00Z",
"activated_at": null,
"config_version": 1,
"proxied_names": 2,
"client_id": null,
"created_at": "2026-03-02T10:20:00Z",
"updated_at": "2026-10-27T09:00:00Z",
"detected_records": 6,
"imported_records": 6,
"import_warnings": []
}Crear zonas en bloque
POST /v1/orgs/{org}/zones/bulkDa de alta hasta 1000 zonas desde un cuerpo CSV: un dominio por línea en la primera columna y, si se quiere, el nombre de un cliente en la segunda (los clientes que no existen se crean). Una cabecera domain o name es opcional y # empieza un comentario.
Cada línea es independiente y sigue las reglas de Crear una zona; la respuesta da el resultado de cada línea. dry_run no está disponible. Los registros públicos solo se importan en altas de hasta 50 dominios.
Rol necesario: tech o superior. No disponible para tokens limitados a una zona.
Controles de cambio: X-Change-Reason (opcional)
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). |
X-Change-Reason | cabecera | string | No | Motivo del cambio (1-500 caracteres UTF-8, sin caracteres de control); por defecto, el alta de la zona. De 1 a 500 caracteres. |
import | consulta | boolean | No | Importa los registros públicos de cada zona delegada (solo en altas de hasta 50 dominios). Por defecto true. |
Cuerpo de la petición
Tipo de contenido text/csv.
Respuesta
200: Resultado por línea. Devuelve un objeto con los campos de abajo.
| Campo | Tipo | Descripción |
|---|---|---|
created | integer | |
failed | integer | |
results | array de object | |
results[].line | integer | |
results[].name | string | Nombre. |
results[].zone | CreatedZone | |
results[].error | string | |
results[].code | string | Código del error de la línea. Uno de invalid, zone_exists, zone_overlaps, zone_limit, forbidden, not_found. |
results[].client | string | Cliente de la línea (segunda columna). |
clients_created | array de string | Clientes creados a partir de la segunda columna. |
Errores
| Estado | Descripción |
|---|---|
400 | CSV inválido o dry_run (no disponible en el alta en bloque). |
422 | Error de validación (invalid) o motivo ausente/no válido (reason_required). |
Ejemplo
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/bulk" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: Onboard new domains" \
-H "Content-Type: text/csv" \
--data-binary $'domain,client\nexample.com,Acme Retail\nexample.org\n'import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.bulkCreateZones({
body: "domain,client\nexample.com,Acme Retail\nexample.org\n"
}, {
reason: "Onboard new domains"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.bulk_create_zones(
body="domain,client\nexample.com,Acme Retail\nexample.org\n",
reason="Onboard new domains",
){
"created": 0,
"failed": 0,
"results": [
{
"line": 0,
"name": "example.com",
"zone": {
"id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
"name": "example.com",
"status": "pending",
"dns_mode": "delegated",
"ns_pair": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"delegation_checked_at": "2026-10-27T09:00:00Z",
"activated_at": null,
"config_version": 1,
"proxied_names": 2,
"client_id": null,
"created_at": "2026-03-02T10:20:00Z",
"updated_at": "2026-10-27T09:00:00Z",
"detected_records": 6,
"imported_records": 6,
"import_warnings": []
},
"error": "",
"code": "invalid",
"client": "example"
}
],
"clients_created": []
}Ver una zona
GET /v1/orgs/{org}/zones/{zone}Devuelve una zona. La cabecera ETag lleva su versión de configuración actual.
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). |
Respuesta
200: Devuelve Zone.
| Campo | Tipo | Descripción |
|---|---|---|
id | string (uuid) | Id. |
org_id | string (uuid) | Id de la organización. |
name | string | Nombre. |
status | string | Uno de pending, active, moved, disabled. |
dns_mode | string | delegated: NS de OpenWAF. external: el cliente conserva su DNS y apunta sus sitios al borde (CNAME/A). Uno de delegated, external. Por defecto delegated. |
ns_pair | array de string | Servidores de nombres que usan las zonas delegadas. Exactamente 2 elementos. |
delegation_checked_at | string (date-time) | Última comprobación de la delegación. |
delegation | CheckResult o VerifyResult | Última comprobación: CheckResult (delegada) o VerifyResult (externa). |
activated_at | string (date-time) | Cuándo pasó a activa. |
moved_at | string (date-time) | Cuándo dejó de estar delegada en OpenWAF. |
disabled_at | string (date-time) | Cuándo se desactivó. |
verification | ZoneVerification | |
cname_target | string | Solo zonas externas: destino del CNAME de cada sitio. |
edge_addresses | EdgeAddresses | |
config_version | integer | Versión de configuración actual (también en ETag). |
proxied_names | integer | Nombres con registro con proxy. |
merged_into | string (uuid) | Solo zonas fusionadas (status disabled): zona padre. |
merged_at | string (date-time) | Solo zonas fusionadas; se purgan a los 14 días. |
created_at | string (date-time) | Fecha de creación. |
updated_at | string (date-time) | Último cambio. |
client_id | string (uuid) o null | Cliente de la organización al que pertenece la zona; null = ninguno. |
Cabeceras de la respuesta: ETag.
Errores
| Estado | Descripción |
|---|---|
404 | No existe o el token no lo ve (not_found). |
Ejemplo
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d" \
-H "Authorization: Bearer $OPENWAF_TOKEN"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.getZone({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.get_zone(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"){
"id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
"name": "example.com",
"status": "active",
"dns_mode": "delegated",
"ns_pair": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"delegation_checked_at": "2026-10-27T09:00:00Z",
"activated_at": "2026-03-02T11:40:00Z",
"config_version": 12,
"proxied_names": 3,
"client_id": null,
"created_at": "2026-03-02T10:20:00Z",
"updated_at": "2026-10-27T09:00:00Z"
}Borrar una zona
DELETE /v1/orgs/{org}/zones/{zone}Borra una zona con sus registros, ajustes de borde y reglas; OpenWAF deja de servir su DNS y sus sitios. Antes se registra una última versión, así que el historial de la zona borrada se sigue pudiendo leer con Listar versiones.
Rol necesario: tech o superior.
Controles de cambio: dry_run · If-Match (opcional) · X-Change-Reason (obligatorio)
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). |
X-Change-Reason | cabecera | string | Sí | Motivo del cambio: 1-500 caracteres UTF-8 tras recortar espacios, sin caracteres de control. Si falta o no es válido: 422 reason_required. De 1 a 500 caracteres. |
If-Match | cabecera | string | No | Versión de configuración esperada, p. ej. "12" (se admite W/). Si no es la actual: 412 version_conflict. |
dry_run | consulta | string | No | 1/true simula el cambio: responde 200 con el plan (Applied) y no guarda nada. Admite 0/false; otro valor: 400. Uno de 1, true, 0, false. |
Respuesta
200: Condry_run=1, el plan del cambio (Applied); no se guarda nada.204: Borrada. Sin cuerpo.
Cabeceras de la respuesta: ETag, X-Config-Version.
Errores
| Estado | Descripción |
|---|---|
400 | Petición mal formada (bad_request). |
404 | No existe o el token no lo ve (not_found). |
412 | version_conflict: If-Match no coincide con la versión actual. |
422 | Error de validación (invalid) o motivo ausente/no válido (reason_required). |
Ejemplo
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: Domain no longer used"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.deleteZone({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
}, {
reason: "Domain no longer used"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.delete_zone(
zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
reason="Domain no longer used",
)Ver el estado de la delegación
GET /v1/orgs/{org}/zones/{zone}/delegationDevuelve el estado de la delegación de una zona delegada (los servidores de nombres que hay que poner en el registrador) o, en una zona externa, el de la verificación de su propiedad.
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). |
Respuesta
200: Devuelve Delegation.
| Campo | Tipo | Descripción |
|---|---|---|
zone_id | string (uuid) | Id de la zona. |
name | string | Nombre. |
status | string | Uno de pending, active, moved, disabled. |
dns_mode | string | delegated: NS de OpenWAF. external: el cliente conserva su DNS y apunta sus sitios al borde (CNAME/A). Uno de delegated, external. Por defecto delegated. |
nameservers | array de string | Los NS que hay que poner en el registrador. |
checked_at | string (date-time) | Última comprobación. |
activated_at | string (date-time) | Cuándo pasó a activa. |
moved_at | string (date-time) | Cuándo dejó de estar delegada en OpenWAF. |
verification | ZoneVerification | |
observation | CheckResult o VerifyResult | CheckResult en zonas delegadas, VerifyResult en externas. |
Ejemplo
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/delegation" \
-H "Authorization: Bearer $OPENWAF_TOKEN"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.getDelegation({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.get_delegation(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"){
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"name": "example.com",
"status": "pending",
"dns_mode": "delegated",
"nameservers": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"checked_at": "2026-10-27T09:30:00Z",
"activated_at": "2026-10-27T09:30:00Z",
"moved_at": "2026-10-27T09:30:00Z",
"verification": {
"name": "_openwaf.example.com",
"type": "A",
"value": "openwaf-verify=3f9c0a1e5b7d4c2a8e6f1b0d9c7a5e3f"
},
"observation": {
"zone": "example.com",
"expected": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"parent_zone": "com",
"verdict": "delegated",
"error": "",
"checked_at": "2026-10-27T09:30:00Z",
"sources": [
{
"kind": "parent",
"server": "example",
"host": "www.example.com",
"ns": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"rcode": "NOERROR",
"error": "",
"match": false
}
]
}
}Comprobar la delegación ahora
POST /v1/orgs/{org}/zones/{zone}/delegation/checkComprueba ahora la delegación de una zona delegada (en los servidores de la zona padre y en resolvedores públicos), o el TXT de verificación de una zona externa, y activa la zona si queda verificada. Lo observado se devuelve en observation.
Rol necesario: tech 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). |
Respuesta
200: Devuelve Delegation.
| Campo | Tipo | Descripción |
|---|---|---|
zone_id | string (uuid) | Id de la zona. |
name | string | Nombre. |
status | string | Uno de pending, active, moved, disabled. |
dns_mode | string | delegated: NS de OpenWAF. external: el cliente conserva su DNS y apunta sus sitios al borde (CNAME/A). Uno de delegated, external. Por defecto delegated. |
nameservers | array de string | Los NS que hay que poner en el registrador. |
checked_at | string (date-time) | Última comprobación. |
activated_at | string (date-time) | Cuándo pasó a activa. |
moved_at | string (date-time) | Cuándo dejó de estar delegada en OpenWAF. |
verification | ZoneVerification | |
observation | CheckResult o VerifyResult | CheckResult en zonas delegadas, VerifyResult en externas. |
Ejemplo
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/delegation/check" \
-H "Authorization: Bearer $OPENWAF_TOKEN"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.checkDelegation({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.check_delegation(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"){
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"name": "example.com",
"status": "pending",
"dns_mode": "delegated",
"nameservers": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"checked_at": "2026-10-27T09:30:00Z",
"activated_at": "2026-10-27T09:30:00Z",
"moved_at": "2026-10-27T09:30:00Z",
"verification": {
"name": "_openwaf.example.com",
"type": "A",
"value": "openwaf-verify=3f9c0a1e5b7d4c2a8e6f1b0d9c7a5e3f"
},
"observation": {
"zone": "example.com",
"expected": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"parent_zone": "com",
"verdict": "delegated",
"error": "",
"checked_at": "2026-10-27T09:30:00Z",
"sources": [
{
"kind": "parent",
"server": "example",
"host": "www.example.com",
"ns": [
"ns1.example-dns.net",
"ns2.example-dns.net"
],
"rcode": "NOERROR",
"error": "",
"match": false
}
]
}
}Comprobar los nombres de una zona externa
GET /v1/orgs/{org}/zones/{zone}/hostsEn una zona externa, comprueba en resolvedores públicos si cada nombre con proxy apunta al borde de OpenWAF:
ok: la cadena CNAME llega acname_targeto todas las direcciones son del borde.proxied_elsewhere: todas las direcciones son de otro proxy (por ejemplo Cloudflare); funciona si el origen de ese proxy es el borde de OpenWAF, pero no se puede confirmar.missing: el nombre no existe o no tiene datos (conerrorsi ningún resolvedor respondió).wrong: cualquier otra cosa.
El resultado es informativo: no cambia cómo se sirve una zona verificada. Se guarda al menos 30 segundos por zona.
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). |
Respuesta
200: Devuelve HostsReport.
| Campo | Tipo | Descripción |
|---|---|---|
checked_at | string (date-time) | Última comprobación. |
items | array de object | |
items[].name | string | Nombre. |
items[].status | string | Uno de ok, proxied_elsewhere, wrong, missing. |
items[].via | string | Uno de cname, a, "". |
items[].answers | array de string | Destinos de la cadena CNAME y después las direcciones. |
items[].error | string |
Errores
| Estado | Descripción |
|---|---|
404 | No existe o el token no lo ve (not_found). |
409 | zone_not_external: la zona es delegada (sus nombres los sirve el DNS de OpenWAF). |
503 | La comprobación no está disponible ahora. |
Ejemplo
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/hosts" \
-H "Authorization: Bearer $OPENWAF_TOKEN"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.listZoneHosts({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.list_zone_hosts(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"){
"checked_at": "2026-10-27T09:30:00Z",
"items": [
{
"name": "example.com",
"status": "ok",
"via": "cname",
"answers": [
"example"
],
"error": ""
}
]
}Fusionar una zona en su padre
POST /v1/orgs/{org}/zones/{zone}/mergeFusiona una zona (por ejemplo tienda.example.com) en su zona padre (into, por ejemplo example.com): sus registros (con los mismos ids) y certificados pasan al padre, los ajustes que difieren pasan a ajustes por nombre en hosts del padre y sus reglas pasan al padre limitadas a los nombres de la hija. Las dos zonas deben estar activas y tener el mismo modo DNS. El padre hereda el cliente de la hija si no tiene; no se fusionan zonas de clientes distintos.
Solo se aplica si cada nombre se sigue sirviendo exactamente igual; si no, responde 409 merge_not_equivalent con el plan. La zona hija queda desactivada (merged_into) y se purga a los 14 días; el padre recibe una versión nueva. En zonas delegadas los nombres se publican en el padre antes de quitar la zona hija, así que nunca dejan de resolver. reason y dry_run del cuerpo tienen prioridad sobre la cabecera y el parámetro de consulta.
Rol necesario: admin o superior. De toda la organización.
Controles de cambio: dry_run · X-Change-Reason (obligatorio)
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). |
X-Change-Reason | cabecera | string | Sí | Motivo del cambio: 1-500 caracteres UTF-8 tras recortar espacios, sin caracteres de control. Si falta o no es válido: 422 reason_required. De 1 a 500 caracteres. |
dry_run | consulta | string | No | 1/true simula el cambio: responde 200 con el plan (Applied) y no guarda nada. Admite 0/false; otro valor: 400. Uno de 1, true, 0, false. |
Cuerpo de la petición
Tipo de contenido application/json. Esquema: MergeInput.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
into | string | Sí | Id o nombre de la zona padre. |
reason | string | No | Gana sobre X-Change-Reason. |
dry_run | boolean | No | Gana sobre ?dry_run. Por defecto false. |
expected_versions | mapa de integer | No | Id o nombre de zona → versión esperada (como If-Match). |
Respuesta
200: Plan (simulación, también si no es aplicable) o fusión aplicada (X-Config-Version y ETag = nueva versión del padre). Devuelve MergeResult.
| Campo | Tipo | Descripción |
|---|---|---|
dry_run | boolean | true en una simulación. |
equivalent | boolean | Todos los nombres se sirven igual antes y después. |
child | MergeZoneRef | |
parent | MergeZoneRef | |
moved | object | |
moved.records | array de object | |
moved.certificates | array de object | |
moved.rules | array de ZoneRule | Reglas creadas en el padre (solo al aplicar). |
moved.hosts | HostSettingsMap | |
moved.client | object | |
hosts | array de object | Informe de equivalencia por nombre. |
hosts[].host | string | |
hosts[].from_zone | string | Uno de child, parent. |
hosts[].equivalent | boolean | |
hosts[].nodes | object | |
hosts[].certificates | object | |
problems | array de string | Motivos por los que no se puede aplicar. |
warnings | array de string | Avisos (el cambio sigue siendo válido). |
diff | Diff | Cambios. |
Cabeceras de la respuesta: ETag, X-Config-Version.
Errores
| Estado | Descripción |
|---|---|
400 | Petición mal formada (bad_request). |
403 | El rol o los límites del token no lo permiten (forbidden). |
404 | No existe o el token no lo ve (not_found). |
409 | merge_not_equivalent: no se aplica; plan lleva el informe. |
412 | version_conflict: If-Match no coincide con la versión actual. |
422 | Error de validación (invalid) o motivo ausente/no válido (reason_required). |
Ejemplo
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/merge" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: Merge shop.example.com into example.com" \
-H "Content-Type: application/json" \
--data '{
"into": "example.com",
"dry_run": true
}'import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.mergeZone({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
body: {
into: "example.com",
dry_run: true
}
}, {
reason: "Merge shop.example.com into example.com"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.merge_zone(
zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
body={
"into": "example.com",
"dry_run": True
},
reason="Merge shop.example.com into example.com",
){
"dry_run": false,
"equivalent": false,
"child": {
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"name": "example.com",
"from_version": 12,
"to_version": 12
},
"parent": {
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"name": "example.com",
"from_version": 12,
"to_version": 12
},
"moved": {
"records": [
{
"id": "a5614527-0ce6-43be-ad1d-d012b7394867",
"type": "A",
"name": "example.com",
"content": "203.0.113.10",
"proxied": false
}
],
"certificates": [
{
"id": "a5614527-0ce6-43be-ad1d-d012b7394867",
"hostname": "www.example.com",
"source": "example",
"fingerprint_sha256": "example",
"not_after": "2026-10-27T09:30:00Z",
"moved": false
}
],
"rules": [
{
"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"
}
],
"hosts": {
"www.example.com": {
"mode": "active",
"waf_level": "off",
"rate_limit_rpm": 0,
"timeout_ms": 0,
"enable_feed": false,
"stack": "generic",
"block_page_mode": "nginx",
"fake_server_header": "example",
"block_page_html": "",
"access_gate": {},
"max_body_mb": 0,
"redirect_allow_hosts": [
"www.example.com"
],
"cors_allow_origins": [
"https://app.example.com"
],
"hsts": {},
"compression": false,
"origin": {},
"cache": {},
"early_hints": {},
"rum": {},
"ai_agents": {},
"rate_rules": []
}
},
"client": {
"from": "75857a45-8999-45be-ac4d-941e90b6b396",
"to": "663ea1bf-ffe5-438f-af0c-f667f14c4257"
}
},
"hosts": [
{
"host": "www.example.com",
"from_zone": "child",
"equivalent": false,
"nodes": {
"before": [],
"after": [],
"lost": [],
"gained": []
},
"certificates": {
"before": [],
"after": []
}
}
],
"problems": [],
"warnings": [],
"diff": {
"from": 0,
"to": 0,
"changes": [
{
"path": "rules[id=12]",
"op": "add",
"from": "example",
"to": "example"
}
]
}
}Deshacer la fusión de una zona
POST /v1/orgs/{org}/zones/{zone}/unmergeDeshace una fusión: el padre vuelve a su versión anterior a la fusión y la zona hija a la suya (registros y certificados vuelven a la hija, que vuelve a estar activa). Si el padre cambió después de la fusión hay que revertir antes esos cambios; si no, la respuesta es 409 merge_not_reversible.
Rol necesario: admin o superior. De toda la organización.
Controles de cambio: dry_run · X-Change-Reason (obligatorio)
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). |
X-Change-Reason | cabecera | string | Sí | Motivo del cambio: 1-500 caracteres UTF-8 tras recortar espacios, sin caracteres de control. Si falta o no es válido: 422 reason_required. De 1 a 500 caracteres. |
dry_run | consulta | string | No | 1/true simula el cambio: responde 200 con el plan (Applied) y no guarda nada. Admite 0/false; otro valor: 400. Uno de 1, true, 0, false. |
Cuerpo de la petición
Tipo de contenido application/json. Esquema: UnmergeInput.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
reason | string | No | Motivo del cambio. |
expected_version | integer | No | Versión del padre que dejó la fusión. |
dry_run | boolean | No | true en una simulación. Por defecto false. |
Respuesta
200: Separada o, condry_run, el plan. Devuelve UnmergeResult.
| Campo | Tipo | Descripción |
|---|---|---|
dry_run | boolean | true en una simulación. |
child | MergeZoneRef | |
parent | MergeZoneRef | |
warnings | array de string | Avisos (el cambio sigue siendo válido). |
Errores
| Estado | Descripción |
|---|---|
400 | Petición mal formada (bad_request). |
403 | El rol o los límites del token no lo permiten (forbidden). |
404 | No existe o el token no lo ve (not_found). |
409 | merge_not_reversible. |
422 | Error de validación (invalid) o motivo ausente/no válido (reason_required). |
Ejemplo
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/unmerge" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: Undo the merge" \
-H "Content-Type: application/json" \
--data '{
"expected_version": 14
}'import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.unmergeZone({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
body: {
expected_version: 14
}
}, {
reason: "Undo the merge"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.unmerge_zone(
zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
body={
"expected_version": 14
},
reason="Undo the merge",
){
"dry_run": false,
"child": {
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"name": "example.com",
"from_version": 12,
"to_version": 12
},
"parent": {
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"name": "example.com",
"from_version": 12,
"to_version": 12
},
"warnings": []
}