# Zonas

> Añade, lista, comprueba y borra zonas (dominios), en modo DNS delegado o externo, y fusiona una zona en su padre.

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](https://developers.openwaf.dev/es/api/zones/#list-zones) | `GET /v1/orgs/{org}/zones` |
| [Crear una zona](https://developers.openwaf.dev/es/api/zones/#create-zone) | `POST /v1/orgs/{org}/zones` |
| [Crear zonas en bloque](https://developers.openwaf.dev/es/api/zones/#bulk-create-zones) | `POST /v1/orgs/{org}/zones/bulk` |
| [Ver una zona](https://developers.openwaf.dev/es/api/zones/#get-zone) | `GET /v1/orgs/{org}/zones/{zone}` |
| [Borrar una zona](https://developers.openwaf.dev/es/api/zones/#delete-zone) | `DELETE /v1/orgs/{org}/zones/{zone}` |
| [Ver el estado de la delegación](https://developers.openwaf.dev/es/api/zones/#get-delegation) | `GET /v1/orgs/{org}/zones/{zone}/delegation` |
| [Comprobar la delegación ahora](https://developers.openwaf.dev/es/api/zones/#check-delegation) | `POST /v1/orgs/{org}/zones/{zone}/delegation/check` |
| [Comprobar los nombres de una zona externa](https://developers.openwaf.dev/es/api/zones/#list-zone-hosts) | `GET /v1/orgs/{org}/zones/{zone}/hosts` |
| [Fusionar una zona en su padre](https://developers.openwaf.dev/es/api/zones/#merge-zone) | `POST /v1/orgs/{org}/zones/{zone}/merge` |
| [Deshacer la fusión de una zona](https://developers.openwaf.dev/es/api/zones/#unmerge-zone) | `POST /v1/orgs/{org}/zones/{zone}/unmerge` |

## Listar zonas

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

Lista 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`: Devuelve `items`, un array de [Zone](https://developers.openwaf.dev/es/api/schemas/#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](https://developers.openwaf.dev/es/api/schemas/#check-result) o [VerifyResult](https://developers.openwaf.dev/es/api/schemas/#verify-result) | Ú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](https://developers.openwaf.dev/es/api/schemas/#zone-verification) |  |
| `items[].cname_target` | string | Solo zonas externas: destino del CNAME de cada sitio. |
| `items[].edge_addresses` | [EdgeAddresses](https://developers.openwaf.dev/es/api/schemas/#edge-addresses) |  |
| `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**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones" \
  -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.listZones();
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.list_zones()
```



```json
{
  "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

```http
POST /v1/orgs/{org}/zones
```

Da 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 sigue `pending` hasta 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 a `cname_target` (o registros A/AAAA a `edge_addresses` en el ápice) y publica el TXT de `verification` para probar la propiedad; una vez verificada, la zona pasa a `active`. 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`](https://developers.openwaf.dev/es/api/#configuration-changes) · [`If-Match`](https://developers.openwaf.dev/es/api/#configuration-changes) (opcional) · [`X-Change-Reason`](https://developers.openwaf.dev/es/api/#configuration-changes) (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`: Con `dry_run=1`, el plan del cambio ([Applied](https://developers.openwaf.dev/es/api/schemas/#applied)); no se guarda nada.
- `201`: Creada. Devuelve [CreatedZone](https://developers.openwaf.dev/es/api/schemas/#created-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](https://developers.openwaf.dev/es/api/schemas/#check-result) o [VerifyResult](https://developers.openwaf.dev/es/api/schemas/#verify-result) | Ú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](https://developers.openwaf.dev/es/api/schemas/#zone-verification) |  |
| `cname_target` | string | Solo zonas externas: destino del CNAME de cada sitio. |
| `edge_addresses` | [EdgeAddresses](https://developers.openwaf.dev/es/api/schemas/#edge-addresses) |  |
| `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**


```sh
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"
  }'
```

**TypeScript**


```ts
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"
});
```

**Python**


```py
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",
)
```



```json
{
  "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

```http
POST /v1/orgs/{org}/zones/bulk
```

Da 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](https://developers.openwaf.dev/es/api/zones/#create-zone); 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`](https://developers.openwaf.dev/es/api/#configuration-changes) (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](https://developers.openwaf.dev/es/api/schemas/#created-zone) |  |
| `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**


```sh
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'
```

**TypeScript**


```ts
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"
});
```

**Python**


```py
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",
)
```



```json
{
  "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

```http
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](https://developers.openwaf.dev/es/api/zones/#list-zones)). |

#### Respuesta

- `200`: Devuelve [Zone](https://developers.openwaf.dev/es/api/schemas/#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](https://developers.openwaf.dev/es/api/schemas/#check-result) o [VerifyResult](https://developers.openwaf.dev/es/api/schemas/#verify-result) | Ú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](https://developers.openwaf.dev/es/api/schemas/#zone-verification) |  |
| `cname_target` | string | Solo zonas externas: destino del CNAME de cada sitio. |
| `edge_addresses` | [EdgeAddresses](https://developers.openwaf.dev/es/api/schemas/#edge-addresses) |  |
| `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**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d" \
  -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.getZone({
  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_zone(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "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

```http
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](https://developers.openwaf.dev/es/api/versions/#list-zone-versions).

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

**Controles de cambio:** [`dry_run`](https://developers.openwaf.dev/es/api/#configuration-changes) · [`If-Match`](https://developers.openwaf.dev/es/api/#configuration-changes) (opcional) · [`X-Change-Reason`](https://developers.openwaf.dev/es/api/#configuration-changes) (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](https://developers.openwaf.dev/es/api/zones/#list-zones)). |
| `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`: Con `dry_run=1`, el plan del cambio ([Applied](https://developers.openwaf.dev/es/api/schemas/#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**


```sh
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"
```

**TypeScript**


```ts
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"
});
```

**Python**


```py
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

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

Devuelve 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](https://developers.openwaf.dev/es/api/zones/#list-zones)). |

#### Respuesta

- `200`: Devuelve [Delegation](https://developers.openwaf.dev/es/api/schemas/#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](https://developers.openwaf.dev/es/api/schemas/#zone-verification) |  |
| `observation` | [CheckResult](https://developers.openwaf.dev/es/api/schemas/#check-result) o [VerifyResult](https://developers.openwaf.dev/es/api/schemas/#verify-result) | CheckResult en zonas delegadas, VerifyResult en externas. |

#### Ejemplo

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/delegation" \
  -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.getDelegation({
  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_delegation(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "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

```http
POST /v1/orgs/{org}/zones/{zone}/delegation/check
```

Comprueba 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](https://developers.openwaf.dev/es/api/zones/#list-zones)). |

#### Respuesta

- `200`: Devuelve [Delegation](https://developers.openwaf.dev/es/api/schemas/#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](https://developers.openwaf.dev/es/api/schemas/#zone-verification) |  |
| `observation` | [CheckResult](https://developers.openwaf.dev/es/api/schemas/#check-result) o [VerifyResult](https://developers.openwaf.dev/es/api/schemas/#verify-result) | CheckResult en zonas delegadas, VerifyResult en externas. |

#### Ejemplo

**curl**


```sh
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/delegation/check" \
  -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.checkDelegation({
  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.check_delegation(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "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

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

En una zona externa, comprueba en resolvedores públicos si cada nombre con proxy apunta al borde de OpenWAF:

- `ok`: la cadena CNAME llega a `cname_target` o 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 (con `error` si 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](https://developers.openwaf.dev/es/api/zones/#list-zones)). |

#### Respuesta

- `200`: Devuelve [HostsReport](https://developers.openwaf.dev/es/api/schemas/#hosts-report).

| 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**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/hosts" \
  -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.listZoneHosts({
  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.list_zone_hosts(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "checked_at": "2026-10-27T09:30:00Z",
  "items": [
    {
      "name": "example.com",
      "status": "ok",
      "via": "cname",
      "answers": [
        "example"
      ],
      "error": ""
    }
  ]
}
```

## Fusionar una zona en su padre

```http
POST /v1/orgs/{org}/zones/{zone}/merge
```

Fusiona 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`](https://developers.openwaf.dev/es/api/#configuration-changes) · [`X-Change-Reason`](https://developers.openwaf.dev/es/api/#configuration-changes) (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](https://developers.openwaf.dev/es/api/zones/#list-zones)). |
| `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](https://developers.openwaf.dev/es/api/schemas/#merge-input).

| 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](https://developers.openwaf.dev/es/api/schemas/#merge-result).

| 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](https://developers.openwaf.dev/es/api/schemas/#merge-zone-ref) |  |
| `parent` | [MergeZoneRef](https://developers.openwaf.dev/es/api/schemas/#merge-zone-ref) |  |
| `moved` | object |  |
| `moved.records` | array de object |  |
| `moved.certificates` | array de object |  |
| `moved.rules` | array de [ZoneRule](https://developers.openwaf.dev/es/api/schemas/#zone-rule) | Reglas creadas en el padre (solo al aplicar). |
| `moved.hosts` | [HostSettingsMap](https://developers.openwaf.dev/es/api/schemas/#host-settings-map) |  |
| `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](https://developers.openwaf.dev/es/api/schemas/#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**


```sh
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
  }'
```

**TypeScript**


```ts
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"
});
```

**Python**


```py
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",
)
```



```json
{
  "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

```http
POST /v1/orgs/{org}/zones/{zone}/unmerge
```

Deshace 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`](https://developers.openwaf.dev/es/api/#configuration-changes) · [`X-Change-Reason`](https://developers.openwaf.dev/es/api/#configuration-changes) (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](https://developers.openwaf.dev/es/api/zones/#list-zones)). |
| `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](https://developers.openwaf.dev/es/api/schemas/#unmerge-input).

| 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, con `dry_run`, el plan. Devuelve [UnmergeResult](https://developers.openwaf.dev/es/api/schemas/#unmerge-result).

| Campo | Tipo | Descripción |
|---|---|---|
| `dry_run` | boolean | `true` en una simulación. |
| `child` | [MergeZoneRef](https://developers.openwaf.dev/es/api/schemas/#merge-zone-ref) |  |
| `parent` | [MergeZoneRef](https://developers.openwaf.dev/es/api/schemas/#merge-zone-ref) |  |
| `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**


```sh
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
  }'
```

**TypeScript**


```ts
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"
});
```

**Python**


```py
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",
)
```



```json
{
  "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": []
}
```
