# Clientes

> Agrupa las zonas por los clientes finales de una agencia y limita miembros y tokens a las zonas de un cliente.

Los clientes son los clientes finales de una agencia. Cada zona puede ser de un cliente, y los miembros, las invitaciones y los tokens de API limitados a un cliente solo ven sus zonas. Estas rutas aún no tienen métodos en los SDK; los ejemplos usan curl.

| Operación | Ruta |
|---|---|
| [Listar clientes](https://developers.openwaf.dev/es/api/clients/#list-clients) | `GET /v1/orgs/{org}/clients` |
| [Crear un cliente](https://developers.openwaf.dev/es/api/clients/#create-client) | `POST /v1/orgs/{org}/clients` |
| [Ver un cliente](https://developers.openwaf.dev/es/api/clients/#get-client) | `GET /v1/orgs/{org}/clients/{client}` |
| [Modificar un cliente](https://developers.openwaf.dev/es/api/clients/#update-client) | `PATCH /v1/orgs/{org}/clients/{client}` |
| [Borrar un cliente](https://developers.openwaf.dev/es/api/clients/#delete-client) | `DELETE /v1/orgs/{org}/clients/{client}` |
| [Asignar el cliente de una zona](https://developers.openwaf.dev/es/api/clients/#set-zone-client) | `PATCH /v1/orgs/{org}/zones/{zone}` |

## Listar clientes

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

Lista los clientes de la organización con su número de zonas.

**Rol necesario:** `readonly` o superior. Un token limitado a un cliente solo ve el suyo.

#### 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`). |

#### Respuesta

- `200`: Devuelve `items`, un array de [Client](https://developers.openwaf.dev/es/api/schemas/#client).

| Campo | Tipo | Descripción |
|---|---|---|
| `items[].id` | string (uuid) | Id. |
| `items[].org_id` | string (uuid) | Id de la organización. |
| `items[].name` | string | Nombre del cliente, único en la organización. |
| `items[].contact_email` | string | Correo de contacto (puede estar vacío). |
| `items[].note` | string | Nota interna de la agencia. |
| `items[].zone_count` | integer | Número de zonas del cliente (en cualquier estado). |
| `items[].created_at` | string (date-time) | Fecha de creación. |
| `items[].updated_at` | string (date-time) | Último cambio. |

#### Errores

| Estado | Descripción |
|---|---|
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |

#### Ejemplo

Aún sin método en los SDK: usa la petición HTTP.

```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/clients" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

```json
{
  "items": [
    {
      "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
      "org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
      "name": "example.com",
      "contact_email": "it@acme.example",
      "note": "Managed by the platform team",
      "zone_count": 128,
      "created_at": "2026-10-27T09:30:00Z",
      "updated_at": "2026-10-27T09:30:00Z"
    }
  ]
}
```

## Crear un cliente

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

Crea un cliente. `name` es obligatorio y único en la organización (sin distinguir mayúsculas).

**Rol necesario:** `tech` o superior. De toda la organización.

#### 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`). |

#### Cuerpo de la petición

Tipo de contenido `application/json`. Esquema: [ClientInput](https://developers.openwaf.dev/es/api/schemas/#client-input).

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `name` | string | No | Obligatorio al crear; sin comas ni saltos de línea. Como mucho 200 caracteres. |
| `contact_email` | string (email) | No | Como mucho 320 caracteres. |
| `note` | string | No | Como mucho 2000 caracteres. |

#### Respuesta

- `201`: Creado. Devuelve [Client](https://developers.openwaf.dev/es/api/schemas/#client).

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string (uuid) | Id. |
| `org_id` | string (uuid) | Id de la organización. |
| `name` | string | Nombre del cliente, único en la organización. |
| `contact_email` | string | Correo de contacto (puede estar vacío). |
| `note` | string | Nota interna de la agencia. |
| `zone_count` | integer | Número de zonas del cliente (en cualquier estado). |
| `created_at` | string (date-time) | Fecha de creación. |
| `updated_at` | string (date-time) | Último cambio. |

#### Errores

| Estado | Descripción |
|---|---|
| `400` | Petición mal formada (`bad_request`). |
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |
| `409` | `conflict`: ya hay un cliente con ese nombre. |
| `422` | Error de validación (`invalid`). |

#### Ejemplo

Aún sin método en los SDK: usa la petición HTTP.

```sh
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/clients" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "name": "Acme Retail",
    "contact_email": "it@acme.example"
  }'
```

```json
{
  "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
  "org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
  "name": "example.com",
  "contact_email": "it@acme.example",
  "note": "Managed by the platform team",
  "zone_count": 128,
  "created_at": "2026-10-27T09:30:00Z",
  "updated_at": "2026-10-27T09:30:00Z"
}
```

## Ver un cliente

```http
GET /v1/orgs/{org}/clients/{client}
```

Devuelve un cliente.

**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`). |
| `client` | ruta | string (uuid) | Sí | Id del cliente. |

#### Respuesta

- `200`: Devuelve [Client](https://developers.openwaf.dev/es/api/schemas/#client).

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string (uuid) | Id. |
| `org_id` | string (uuid) | Id de la organización. |
| `name` | string | Nombre del cliente, único en la organización. |
| `contact_email` | string | Correo de contacto (puede estar vacío). |
| `note` | string | Nota interna de la agencia. |
| `zone_count` | integer | Número de zonas del cliente (en cualquier estado). |
| `created_at` | string (date-time) | Fecha de creación. |
| `updated_at` | string (date-time) | Último cambio. |

#### Errores

| Estado | Descripción |
|---|---|
| `404` | No existe o el token no lo ve (`not_found`). |

#### Ejemplo

Aún sin método en los SDK: usa la petición HTTP.

```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/clients/7c1e3a5d-9b2f-4d6a-8e0c-2f4a6c8e0b1d" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

```json
{
  "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
  "org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
  "name": "example.com",
  "contact_email": "it@acme.example",
  "note": "Managed by the platform team",
  "zone_count": 128,
  "created_at": "2026-10-27T09:30:00Z",
  "updated_at": "2026-10-27T09:30:00Z"
}
```

## Modificar un cliente

```http
PATCH /v1/orgs/{org}/clients/{client}
```

Cambia el nombre, el correo de contacto o la nota de un cliente. Los campos ausentes conservan su valor.

**Rol necesario:** `admin` o superior. De toda la organización.

#### 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`). |
| `client` | ruta | string (uuid) | Sí | Id del cliente. |

#### Cuerpo de la petición

Tipo de contenido `application/json`. Esquema: [ClientInput](https://developers.openwaf.dev/es/api/schemas/#client-input).

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `name` | string | No | Obligatorio al crear; sin comas ni saltos de línea. Como mucho 200 caracteres. |
| `contact_email` | string (email) | No | Como mucho 320 caracteres. |
| `note` | string | No | Como mucho 2000 caracteres. |

#### Respuesta

- `200`: Devuelve [Client](https://developers.openwaf.dev/es/api/schemas/#client).

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string (uuid) | Id. |
| `org_id` | string (uuid) | Id de la organización. |
| `name` | string | Nombre del cliente, único en la organización. |
| `contact_email` | string | Correo de contacto (puede estar vacío). |
| `note` | string | Nota interna de la agencia. |
| `zone_count` | integer | Número de zonas del cliente (en cualquier estado). |
| `created_at` | string (date-time) | Fecha de creación. |
| `updated_at` | string (date-time) | Último cambio. |

#### 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` | `conflict`: ya hay un cliente con ese nombre. |
| `422` | Error de validación (`invalid`). |

#### Ejemplo

Aún sin método en los SDK: usa la petición HTTP.

```sh
curl -X PATCH "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/clients/7c1e3a5d-9b2f-4d6a-8e0c-2f4a6c8e0b1d" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "note": "Renewal in March"
  }'
```

```json
{
  "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
  "org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
  "name": "example.com",
  "contact_email": "it@acme.example",
  "note": "Managed by the platform team",
  "zone_count": 128,
  "created_at": "2026-10-27T09:30:00Z",
  "updated_at": "2026-10-27T09:30:00Z"
}
```

## Borrar un cliente

```http
DELETE /v1/orgs/{org}/clients/{client}
```

Borra un cliente sin zonas. Antes hay que mover sus zonas a otro cliente (o a ninguno) con [Asignar el cliente de una zona](https://developers.openwaf.dev/es/api/clients/#set-zone-client), o borrarlas.

**Rol necesario:** `admin` o superior. De toda la organización.

#### 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`). |
| `client` | ruta | string (uuid) | Sí | Id del cliente. |

#### Respuesta

- `204`: Borrado. Sin cuerpo.

#### Errores

| Estado | Descripción |
|---|---|
| `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` | `client_has_zones`: el cliente aún tiene zonas; muévelas a otro cliente o bórralas antes. |

#### Ejemplo

Aún sin método en los SDK: usa la petición HTTP.

```sh
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/clients/7c1e3a5d-9b2f-4d6a-8e0c-2f4a6c8e0b1d" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

## Asignar el cliente de una zona

```http
PATCH /v1/orgs/{org}/zones/{zone}
```

Asigna una zona a un cliente, o a ninguno con `null`. El cliente forma parte de la configuración de la zona, así que el cambio crea una versión.

**Rol necesario:** `tech` o superior. De toda la organización.

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

#### Cuerpo de la petición

Tipo de contenido `application/json`.

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `client_id` | string (uuid) o null | Sí | Id del cliente, o null para que la zona no sea de ningún cliente. |

#### Respuesta

- `200`: Zona modificada (con X-Config-Version y ETag) o, con `dry_run`, el plan (`Applied`). Devuelve [Zone](https://developers.openwaf.dev/es/api/schemas/#zone) o [Applied](https://developers.openwaf.dev/es/api/schemas/#applied).

| 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`, `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`). |
| `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

Aún sin método en los SDK: usa la petición HTTP.

```sh
curl -X PATCH "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "X-Change-Reason: Move the zone to its client" \
  -H 'If-Match: "12"' \
  -H "Content-Type: application/json" \
  --data '{
    "client_id": "7c1e3a5d-9b2f-4d6a-8e0c-2f4a6c8e0b1d"
  }'
```

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