# Egress

> Gestiona OpenWAF Egress, la pasarela entre tus agentes IA y sus proveedores, con destinos, credenciales, agentes y eventos.

OpenWAF Egress se sitúa entre tus agentes IA y sus proveedores. Los agentes llaman a la pasarela con un token sustituto y la pasarela añade la credencial real del proveedor, inspecciona la petición y bloquea inyecciones, fugas de datos y destinos no permitidos. Leer exige el rol `readonly` y escribir `admin`, ambos de toda la organización; crear y cambiar agentes, destinos y credenciales y leer los eventos exige que la organización tenga activada la función egress (si no, 403 `feature_disabled`). Consulta [OpenWAF Egress](https://developers.openwaf.dev/es/egress/).

| Operación | Ruta |
|---|---|
| [Ver el estado del egress](https://developers.openwaf.dev/es/api/egress/#get-egress) | `GET /v1/orgs/{org}/egress` |
| [Listar destinos](https://developers.openwaf.dev/es/api/egress/#list-egress-targets) | `GET /v1/orgs/{org}/egress/targets` |
| [Crear un destino](https://developers.openwaf.dev/es/api/egress/#create-egress-target) | `POST /v1/orgs/{org}/egress/targets` |
| [Borrar un destino](https://developers.openwaf.dev/es/api/egress/#delete-egress-target) | `DELETE /v1/orgs/{org}/egress/targets/{target}` |
| [Guardar la credencial de un destino](https://developers.openwaf.dev/es/api/egress/#put-egress-credential) | `PUT /v1/orgs/{org}/egress/targets/{target}/credential` |
| [Borrar la credencial de un destino](https://developers.openwaf.dev/es/api/egress/#delete-egress-credential) | `DELETE /v1/orgs/{org}/egress/targets/{target}/credential` |
| [Listar agentes](https://developers.openwaf.dev/es/api/egress/#list-egress-agents) | `GET /v1/orgs/{org}/egress/agents` |
| [Crear un agente](https://developers.openwaf.dev/es/api/egress/#create-egress-agent) | `POST /v1/orgs/{org}/egress/agents` |
| [Ver un agente](https://developers.openwaf.dev/es/api/egress/#get-egress-agent) | `GET /v1/orgs/{org}/egress/agents/{agent}` |
| [Modificar un agente](https://developers.openwaf.dev/es/api/egress/#update-egress-agent) | `PATCH /v1/orgs/{org}/egress/agents/{agent}` |
| [Borrar un agente](https://developers.openwaf.dev/es/api/egress/#delete-egress-agent) | `DELETE /v1/orgs/{org}/egress/agents/{agent}` |
| [Rotar el token de un agente](https://developers.openwaf.dev/es/api/egress/#rotate-egress-agent-token) | `POST /v1/orgs/{org}/egress/agents/{agent}/rotate-token` |
| [Listar eventos de egress](https://developers.openwaf.dev/es/api/egress/#list-egress-events) | `GET /v1/orgs/{org}/egress/events` |
| [Stream de eventos de egress](https://developers.openwaf.dev/es/api/egress/#stream-egress-events) | `GET /v1/orgs/{org}/egress/events/stream` |

## Ver el estado del egress

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

Devuelve el estado del egress de la organización. Se puede leer sin la función egress (`enabled` a falso). Sin la función, leer y borrar agentes, destinos y credenciales sigue funcionando para que la organización pueda retirar sus claves.

**Rol necesario:** `readonly` 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`). |

#### Respuesta

- `200`: Devuelve [EgressOverview](https://developers.openwaf.dev/es/api/schemas/#egress-overview).

| Campo | Tipo | Descripción |
|---|---|---|
| `enabled` | boolean | La organización tiene la función egress. |
| `gateway_url` | string |  |
| `credentials_available` | boolean | Ahora se pueden guardar credenciales. |

#### Errores

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

#### Ejemplo

**curl**


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

**Python**


```py
from openwaf import OpenWAF

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



```json
{
  "enabled": false,
  "gateway_url": "https://egress.openwaf.dev",
  "credentials_available": false
}
```

## Listar destinos

```http
GET /v1/orgs/{org}/egress/targets
```

Lista los destinos de egress, los proveedores del catálogo y los propios, con el estado de su credencial.

**Rol necesario:** `readonly` 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`). |

#### Respuesta

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

| Campo | Tipo | Descripción |
|---|---|---|
| `items[].id` | string | Id. |
| `items[].name` | string | Nombre. |
| `items[].base_url` | string |  |
| `items[].host` | string |  |
| `items[].auth_header` | string |  |
| `items[].auth_prefix` | string |  |
| `items[].custom` | boolean | Destino propio de la organización (false = catálogo). |
| `items[].docs_url` | string |  |
| `items[].credential` | [EgressCredential](https://developers.openwaf.dev/es/api/schemas/#egress-credential) |  |

#### Errores

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

#### Ejemplo

**curl**


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

**Python**


```py
from openwaf import OpenWAF

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



```json
{
  "items": [
    {
      "id": "openrouter",
      "name": "example.com",
      "base_url": "https://llm.example.com/v1",
      "host": "www.example.com",
      "auth_header": "Authorization",
      "auth_prefix": "Bearer ",
      "custom": false,
      "docs_url": "https://platform.openai.com/docs/api-reference",
      "credential": {
        "fingerprint": "sha256:3f9a1c2b7d4e",
        "updated_at": "2026-10-27T09:30:00Z",
        "updated_by": "token:11111111-2222-4333-8444-555555555555"
      }
    }
  ]
}
```

## Crear un destino

```http
POST /v1/orgs/{org}/egress/targets
```

Crea un destino propio HTTPS. `base_url` debe ser `https://` en el puerto 443 con un nombre DNS público (sin dirección IP, usuario, consulta ni fragmento). En cada petición la pasarela comprueba además que el nombre solo resuelve a direcciones públicas (`ssrf_blocked`).

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

#### Cuerpo de la petición

Tipo de contenido `application/json`.

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `id` | string | Sí | No puede ser un id del catálogo. Patrón `^[a-z][a-z0-9-]{1,31}$`. |
| `name` | string | Sí | Nombre. De 1 a 100 caracteres. |
| `base_url` | string | Sí | Como mucho 2048 caracteres. |
| `auth_header` | string | No | Por defecto `Authorization`. Patrón `^[A-Za-z0-9-]{1,64}$`. |
| `auth_prefix` | string | No | Por defecto `Bearer ` con Authorization y vacío con otras cabeceras. Como mucho 32 caracteres. |

#### Respuesta

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

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string | Id. |
| `name` | string | Nombre. |
| `base_url` | string |  |
| `host` | string |  |
| `auth_header` | string |  |
| `auth_prefix` | string |  |
| `custom` | boolean | Destino propio de la organización (false = catálogo). |
| `docs_url` | string |  |
| `credential` | [EgressCredential](https://developers.openwaf.dev/es/api/schemas/#egress-credential) |  |

#### Errores

| Estado | Descripción |
|---|---|
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |
| `409` | El estado actual no lo permite (`conflict`). |
| `422` | Error de validación (`invalid`). |

#### Ejemplo

**curl**


```sh
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/targets" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "id": "internal-llm",
    "name": "Internal LLM",
    "base_url": "https://llm.example.com/v1"
  }'
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.createEgressTarget({
  body: {
    id: "internal-llm",
    name: "Internal LLM",
    base_url: "https://llm.example.com/v1"
  }
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.create_egress_target(
    body={
        "id": "internal-llm",
        "name": "Internal LLM",
        "base_url": "https://llm.example.com/v1"
    },
)
```



```json
{
  "id": "openrouter",
  "name": "example.com",
  "base_url": "https://llm.example.com/v1",
  "host": "www.example.com",
  "auth_header": "Authorization",
  "auth_prefix": "Bearer ",
  "custom": false,
  "docs_url": "https://platform.openai.com/docs/api-reference",
  "credential": {
    "fingerprint": "sha256:3f9a1c2b7d4e",
    "updated_at": "2026-10-27T09:30:00Z",
    "updated_by": "token:11111111-2222-4333-8444-555555555555"
  }
}
```

## Borrar un destino

```http
DELETE /v1/orgs/{org}/egress/targets/{target}
```

Borra un destino propio y su credencial. Los destinos del catálogo no se pueden borrar.

**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`). |
| `target` | ruta | string | Sí | Id del destino de egress: un id del catálogo como `openai` o el de un destino propio. Patrón `^[a-z][a-z0-9-]{1,31}$`. |

#### 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` | `target_in_use`: algún agente lo permite. |
| `422` | `invalid`: los destinos del catálogo no se borran. |

#### Ejemplo

**curl**


```sh
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/targets/openai" \
  -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.deleteEgressTarget({
  target: "openai"
});
```

**Python**


```py
from openwaf import OpenWAF

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



## Guardar la credencial de un destino

```http
PUT /v1/orgs/{org}/egress/targets/{target}/credential
```

Guarda o sustituye la credencial del proveedor de un destino. Es de solo escritura, se guarda en un almacén de secretos dedicado, nunca se registra ni se devuelve; la respuesta solo lleva su huella (`sha256:` y 12 caracteres hexadecimales).

**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`). |
| `target` | ruta | string | Sí | Id del destino de egress: un id del catálogo como `openai` o el de un destino propio. Patrón `^[a-z][a-z0-9-]{1,31}$`. |

#### Cuerpo de la petición

Tipo de contenido `application/json`.

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `secret` | string | Sí | De 1 a 8192 caracteres. |

#### Respuesta

- `200`: Devuelve [EgressTarget](https://developers.openwaf.dev/es/api/schemas/#egress-target).

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string | Id. |
| `name` | string | Nombre. |
| `base_url` | string |  |
| `host` | string |  |
| `auth_header` | string |  |
| `auth_prefix` | string |  |
| `custom` | boolean | Destino propio de la organización (false = catálogo). |
| `docs_url` | string |  |
| `credential` | [EgressCredential](https://developers.openwaf.dev/es/api/schemas/#egress-credential) |  |

#### 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`). |
| `422` | Error de validación (`invalid`). |
| `502` | Una dependencia lo rechazó o no respondió; reinténtalo más tarde. |
| `503` | No disponible ahora (`unavailable`); reinténtalo más tarde. |

#### Ejemplo

**curl**


```sh
curl -X PUT "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/targets/openai/credential" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "secret": "sk-…"
  }'
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.putEgressCredential({
  target: "openai",
  body: {
    secret: "sk-…"
  }
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.put_egress_credential(
    target="openai",
    body={
        "secret": "sk-…"
    },
)
```



```json
{
  "id": "openrouter",
  "name": "example.com",
  "base_url": "https://llm.example.com/v1",
  "host": "www.example.com",
  "auth_header": "Authorization",
  "auth_prefix": "Bearer ",
  "custom": false,
  "docs_url": "https://platform.openai.com/docs/api-reference",
  "credential": {
    "fingerprint": "sha256:3f9a1c2b7d4e",
    "updated_at": "2026-10-27T09:30:00Z",
    "updated_by": "token:11111111-2222-4333-8444-555555555555"
  }
}
```

## Borrar la credencial de un destino

```http
DELETE /v1/orgs/{org}/egress/targets/{target}/credential
```

Borra la credencial del proveedor de un destino, todas sus versiones guardadas. Es idempotente y también funciona sin la función egress.

**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`). |
| `target` | ruta | string | Sí | Id del destino de egress: un id del catálogo como `openai` o el de un destino propio. Patrón `^[a-z][a-z0-9-]{1,31}$`. |

#### Respuesta

- `204`: Borrada (o no existía). 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`). |
| `502` | Una dependencia lo rechazó o no respondió; reinténtalo más tarde. |
| `503` | No disponible ahora (`unavailable`); reinténtalo más tarde. |

#### Ejemplo

**curl**


```sh
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/targets/openai/credential" \
  -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.deleteEgressCredential({
  target: "openai"
});
```

**Python**


```py
from openwaf import OpenWAF

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



## Listar agentes

```http
GET /v1/orgs/{org}/egress/agents
```

Lista los agentes de egress de la organización.

**Rol necesario:** `readonly` 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`). |

#### Respuesta

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

| Campo | Tipo | Descripción |
|---|---|---|
| `items[].id` | string (uuid) | Id. |
| `items[].name` | string | Nombre. |
| `items[].description` | string |  |
| `items[].allowed_targets` | array de string |  |
| `items[].enforce_injection` | boolean |  |
| `items[].block_dlp` | boolean |  |
| `items[].disabled` | boolean |  |
| `items[].token_prefix` | string | Comienzo del token (`OWE_TOKEN_` y 6 caracteres) para reconocerlo. |
| `items[].token_rotated_at` | string (date-time) |  |
| `items[].created_at` | string (date-time) | Fecha de creación. |
| `items[].created_by` | string | Quién lo creó (actor). |
| `items[].updated_at` | string (date-time) | Último cambio. |
| `items[].base_urls` | mapa de string | URL base del SDK para cada destino permitido. |

#### Errores

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

#### Ejemplo

**curl**


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

**Python**


```py
from openwaf import OpenWAF

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



```json
{
  "items": [
    {
      "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
      "name": "example.com",
      "description": "Support assistant",
      "allowed_targets": [
        "openai"
      ],
      "enforce_injection": false,
      "block_dlp": false,
      "disabled": false,
      "token_prefix": "OWE_TOKEN_ab12cd",
      "token_rotated_at": "2026-10-27T09:30:00Z",
      "created_at": "2026-10-27T09:30:00Z",
      "created_by": "token:11111111-2222-4333-8444-555555555555",
      "updated_at": "2026-10-27T09:30:00Z",
      "base_urls": {
        "openrouter": "https://egress.openwaf.dev/v1/agent/support-bot/proxy/openrouter"
      }
    }
  ]
}
```

## Crear un agente

```http
POST /v1/orgs/{org}/egress/agents
```

Crea un agente que puede llamar a algunos destinos a través de la pasarela. Su `token` sustituto solo se devuelve en esta respuesta. `name` forma parte de las URL de la pasarela del agente y no se puede cambiar.

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

#### Cuerpo de la petición

Tipo de contenido `application/json`.

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `name` | string | Sí | Único en la organización; forma parte de sus URL de la pasarela y no cambia. Patrón `^[a-z0-9][a-z0-9-]{0,62}$`. |
| `description` | string | No | Como mucho 500 caracteres. |
| `allowed_targets` | array de string | Sí | De 1 a 32 elementos. |
| `enforce_injection` | boolean | No | Por defecto `true`. |
| `block_dlp` | boolean | No | Por defecto `true`. |
| `disabled` | boolean | No | Por defecto `false`. |

#### Respuesta

- `201`: Creado; `token` se muestra una sola vez. Devuelve [IssuedEgressAgent](https://developers.openwaf.dev/es/api/schemas/#issued-egress-agent).

| Campo | Tipo | Descripción |
|---|---|---|
| `agent` | [EgressAgent](https://developers.openwaf.dev/es/api/schemas/#egress-agent) |  |
| `token` | string | Token sustituto `OWE_TOKEN_…`; se muestra una sola vez (solo se guarda su SHA-256). |

#### Errores

| Estado | Descripción |
|---|---|
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |
| `409` | El estado actual no lo permite (`conflict`). |
| `422` | Error de validación (`invalid`). |

#### Ejemplo

**curl**


```sh
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/agents" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "name": "support-bot",
    "allowed_targets": [
      "openai"
    ]
  }'
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.createEgressAgent({
  body: {
    name: "support-bot",
    allowed_targets: [
      "openai"
    ]
  }
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.create_egress_agent(
    body={
        "name": "support-bot",
        "allowed_targets": [
            "openai"
        ]
    },
)
```



```json
{
  "agent": {
    "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
    "name": "example.com",
    "description": "Support assistant",
    "allowed_targets": [
      "openai"
    ],
    "enforce_injection": false,
    "block_dlp": false,
    "disabled": false,
    "token_prefix": "OWE_TOKEN_ab12cd",
    "token_rotated_at": "2026-10-27T09:30:00Z",
    "created_at": "2026-10-27T09:30:00Z",
    "created_by": "token:11111111-2222-4333-8444-555555555555",
    "updated_at": "2026-10-27T09:30:00Z",
    "base_urls": {
      "openrouter": "https://egress.openwaf.dev/v1/agent/support-bot/proxy/openrouter"
    }
  },
  "token": "owf_…"
}
```

## Ver un agente

```http
GET /v1/orgs/{org}/egress/agents/{agent}
```

Devuelve un agente de egress.

**Rol necesario:** `readonly` 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`). |
| `agent` | ruta | string (uuid) | Sí | Id del agente de egress. |

#### Respuesta

- `200`: Devuelve [EgressAgent](https://developers.openwaf.dev/es/api/schemas/#egress-agent).

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string (uuid) | Id. |
| `name` | string | Nombre. |
| `description` | string |  |
| `allowed_targets` | array de string |  |
| `enforce_injection` | boolean |  |
| `block_dlp` | boolean |  |
| `disabled` | boolean |  |
| `token_prefix` | string | Comienzo del token (`OWE_TOKEN_` y 6 caracteres) para reconocerlo. |
| `token_rotated_at` | string (date-time) |  |
| `created_at` | string (date-time) | Fecha de creación. |
| `created_by` | string | Quién lo creó (actor). |
| `updated_at` | string (date-time) | Último cambio. |
| `base_urls` | mapa de string | URL base del SDK para cada destino permitido. |

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

#### Ejemplo

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/agents/4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c" \
  -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.getEgressAgent({
  agent: "4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.get_egress_agent(agent="4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c")
```



```json
{
  "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
  "name": "example.com",
  "description": "Support assistant",
  "allowed_targets": [
    "openai"
  ],
  "enforce_injection": false,
  "block_dlp": false,
  "disabled": false,
  "token_prefix": "OWE_TOKEN_ab12cd",
  "token_rotated_at": "2026-10-27T09:30:00Z",
  "created_at": "2026-10-27T09:30:00Z",
  "created_by": "token:11111111-2222-4333-8444-555555555555",
  "updated_at": "2026-10-27T09:30:00Z",
  "base_urls": {
    "openrouter": "https://egress.openwaf.dev/v1/agent/support-bot/proxy/openrouter"
  }
}
```

## Modificar un agente

```http
PATCH /v1/orgs/{org}/egress/agents/{agent}
```

Cambia un agente. Los campos ausentes conservan su valor; el nombre no se puede cambiar.

**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`). |
| `agent` | ruta | string (uuid) | Sí | Id del agente de egress. |

#### Cuerpo de la petición

Tipo de contenido `application/json`.

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `description` | string | No | Como mucho 500 caracteres. |
| `allowed_targets` | array de string | No | De 1 a 32 elementos. |
| `enforce_injection` | boolean | No |  |
| `block_dlp` | boolean | No |  |
| `disabled` | boolean | No |  |

#### Respuesta

- `200`: Devuelve [EgressAgent](https://developers.openwaf.dev/es/api/schemas/#egress-agent).

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string (uuid) | Id. |
| `name` | string | Nombre. |
| `description` | string |  |
| `allowed_targets` | array de string |  |
| `enforce_injection` | boolean |  |
| `block_dlp` | boolean |  |
| `disabled` | boolean |  |
| `token_prefix` | string | Comienzo del token (`OWE_TOKEN_` y 6 caracteres) para reconocerlo. |
| `token_rotated_at` | string (date-time) |  |
| `created_at` | string (date-time) | Fecha de creación. |
| `created_by` | string | Quién lo creó (actor). |
| `updated_at` | string (date-time) | Último cambio. |
| `base_urls` | mapa de string | URL base del SDK para cada destino permitido. |

#### 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`). |
| `422` | Error de validación (`invalid`). |

#### Ejemplo

**curl**


```sh
curl -X PATCH "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/agents/4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "allowed_targets": [
      "openai",
      "anthropic"
    ]
  }'
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.updateEgressAgent({
  agent: "4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c",
  body: {
    allowed_targets: [
      "openai",
      "anthropic"
    ]
  }
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.update_egress_agent(
    agent="4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c",
    body={
        "allowed_targets": [
            "openai",
            "anthropic"
        ]
    },
)
```



```json
{
  "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
  "name": "example.com",
  "description": "Support assistant",
  "allowed_targets": [
    "openai"
  ],
  "enforce_injection": false,
  "block_dlp": false,
  "disabled": false,
  "token_prefix": "OWE_TOKEN_ab12cd",
  "token_rotated_at": "2026-10-27T09:30:00Z",
  "created_at": "2026-10-27T09:30:00Z",
  "created_by": "token:11111111-2222-4333-8444-555555555555",
  "updated_at": "2026-10-27T09:30:00Z",
  "base_urls": {
    "openrouter": "https://egress.openwaf.dev/v1/agent/support-bot/proxy/openrouter"
  }
}
```

## Borrar un agente

```http
DELETE /v1/orgs/{org}/egress/agents/{agent}
```

Borra un agente de egress; su token deja de valer.

**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`). |
| `agent` | ruta | string (uuid) | Sí | Id del agente de egress. |

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

#### Ejemplo

**curl**


```sh
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/agents/4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c" \
  -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.deleteEgressAgent({
  agent: "4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.delete_egress_agent(agent="4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c")
```



## Rotar el token de un agente

```http
POST /v1/orgs/{org}/egress/agents/{agent}/rotate-token
```

Da al agente un token sustituto nuevo, que solo se devuelve en esta respuesta. El anterior deja de valer en cuanto las pasarelas cargan la nueva configuración.

**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`). |
| `agent` | ruta | string (uuid) | Sí | Id del agente de egress. |

#### Respuesta

- `200`: OK; `token` se muestra una sola vez. Devuelve [IssuedEgressAgent](https://developers.openwaf.dev/es/api/schemas/#issued-egress-agent).

| Campo | Tipo | Descripción |
|---|---|---|
| `agent` | [EgressAgent](https://developers.openwaf.dev/es/api/schemas/#egress-agent) |  |
| `token` | string | Token sustituto `OWE_TOKEN_…`; se muestra una sola vez (solo se guarda su SHA-256). |

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

#### Ejemplo

**curl**


```sh
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/agents/4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c/rotate-token" \
  -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.rotateEgressAgentToken({
  agent: "4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.rotate_egress_agent_token(agent="4a2c6e8b-0d1f-4c3e-9b5a-7d9f1b3e5a7c")
```



```json
{
  "agent": {
    "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
    "name": "example.com",
    "description": "Support assistant",
    "allowed_targets": [
      "openai"
    ],
    "enforce_injection": false,
    "block_dlp": false,
    "disabled": false,
    "token_prefix": "OWE_TOKEN_ab12cd",
    "token_rotated_at": "2026-10-27T09:30:00Z",
    "created_at": "2026-10-27T09:30:00Z",
    "created_by": "token:11111111-2222-4333-8444-555555555555",
    "updated_at": "2026-10-27T09:30:00Z",
    "base_urls": {
      "openrouter": "https://egress.openwaf.dev/v1/agent/support-bot/proxy/openrouter"
    }
  },
  "token": "owf_…"
}
```

## Listar eventos de egress

```http
GET /v1/orgs/{org}/egress/events
```

Lista las peticiones bloqueadas por la pasarela de egress, de la más reciente a la más antigua, con los mismos filtros y forma que [Listar eventos](https://developers.openwaf.dev/es/api/security-events/#list-events). Cada bloqueo es un evento con `action` `block`, `attack_type` `egress`, el código en `rule_name` (`injection_detected`, `data_leak_blocked`, `ssrf_blocked`, `egress_blocked`, `body_too_large`), el destino en `host`, el nombre del agente en `user_agent` y los identificadores de detección en `msg`. Nunca incluye el cuerpo de la petición.

**Rol necesario:** `readonly` o superior. De toda la organización. Exige la función egress.

#### 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`). |
| `since` | consulta | string (date-time) | No | RFC 3339; por defecto until - 24 h. |
| `until` | consulta | string (date-time) | No | RFC 3339, exclusivo; por defecto ahora. Rango máximo 31 días. |
| `action` | consulta | string | No | Repetible (cualquiera de los valores, como mucho 20). Uno de `block`, `log`, `challenge`, `allow`. |
| `ip` | consulta | string | No | IPv4 o IPv6 (se normaliza); repetible. |
| `host` | consulta | string | No | Nombre exacto (sin distinguir mayúsculas); repetible. De 1 a 253 caracteres. |
| `path` | consulta | string | No | Ruta exacta (1-2048 bytes); repetible. De 1 a 2048 caracteres. |
| `status` | consulta | integer | No | Estado HTTP de la respuesta; repetible. De 100 a 599. |
| `rule_name` | consulta | string | No | rule_name exacto (el código de un bloqueo de egress, p. ej. `injection_detected`); repetible. De 1 a 200 caracteres. |
| `not_action` | consulta | string | No | Excluye esas acciones; repetible. Uno de `block`, `log`, `challenge`, `allow`. |
| `not_ip` | consulta | string | No | Excluye esas IP; repetible. |
| `not_host` | consulta | string | No | Excluye esos nombres; repetible. De 1 a 253 caracteres. |
| `not_path` | consulta | string | No | Excluye esas rutas exactas; repetible. De 1 a 2048 caracteres. |
| `not_status` | consulta | integer | No | Excluye esos estados HTTP; repetible. De 100 a 599. |
| `not_rule_name` | consulta | string | No | Excluye esos rule_name; repetible. De 1 a 200 caracteres. |
| `q` | consulta | string | No | Texto libre (sin distinguir mayúsculas) en host, ruta, IP, método, rule_name, attack_type, msg, user agent, país, rule_id y status. Como mucho 200 caracteres. |
| `limit` | consulta | integer | No | Número máximo de elementos. Por defecto `100`. De 1 a 1000. |
| `cursor` | consulta | string | No | next_cursor de la página anterior. |

#### Respuesta

- `200`: Devuelve un objeto con los campos de abajo.

| Campo | Tipo | Descripción |
|---|---|---|
| `items` | array de [EdgeEvent](https://developers.openwaf.dev/es/api/schemas/#edge-event) |  |
| `next_cursor` | string |  |

#### Errores

| Estado | Descripción |
|---|---|
| `400` | Petición mal formada (`bad_request`). |
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |
| `503` | No disponible ahora (`unavailable`); reinténtalo más tarde. |

#### Ejemplo

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/egress/events?rule_name=injection_detected" \
  -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.listEgressEvents({
  rule_name: "injection_detected"
});
```

**Python**


```py
from openwaf import OpenWAF

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



```json
{
  "items": [
    {
      "event_id": "8b3e5f7a-1c2d-4e6f-9a0b-3c5d7e9f1a2b",
      "zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
      "node_id": "edge-node",
      "ts": "2026-10-27T09:29:58Z",
      "host": "www.example.com",
      "ip": "203.0.113.10",
      "method": "POST",
      "path": "/login",
      "status": 429,
      "action": "block",
      "rule_id": 0,
      "rule_name": "rate_rule:login",
      "attack_type": "rate_limit",
      "msg": "",
      "country_code": "ES",
      "asn": 64500,
      "user_agent": "Mozilla/5.0",
      "tls_fingerprint": "",
      "received_at": "2026-10-27T09:29:59Z",
      "rate_rule": "login"
    }
  ],
  "next_cursor": ""
}
```

## Stream de eventos de egress

```http
GET /v1/orgs/{org}/egress/events/stream
```

Emite como Server-Sent Events los bloqueos de la pasarela de egress, con las mismas tramas, reanudación y límites que [Stream de eventos](https://developers.openwaf.dev/es/api/security-events/#stream-events) y los eventos de [Listar eventos de egress](https://developers.openwaf.dev/es/api/egress/#list-egress-events).

**Rol necesario:** `readonly` o superior. De toda la organización. Exige la función egress.

#### 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`). |
| `Last-Event-ID` | cabecera | string | No | `id` del último evento recibido de un stream; tiene prioridad sobre `cursor`. |
| `cursor` | consulta | string | No | `id` de un evento de un stream anterior: se empieza justo después. |
| `action` | consulta | string | No | Repetible (cualquiera de los valores, como mucho 20). Uno de `block`, `log`, `challenge`, `allow`. |
| `ip` | consulta | string | No | IPv4 o IPv6 (se normaliza); repetible. |
| `host` | consulta | string | No | Nombre exacto (sin distinguir mayúsculas); repetible. De 1 a 253 caracteres. |
| `path` | consulta | string | No | Ruta exacta (1-2048 bytes); repetible. De 1 a 2048 caracteres. |
| `status` | consulta | integer | No | Estado HTTP de la respuesta; repetible. De 100 a 599. |
| `rule_name` | consulta | string | No | rule_name exacto (el código de un bloqueo de egress, p. ej. `injection_detected`); repetible. De 1 a 200 caracteres. |
| `not_action` | consulta | string | No | Excluye esas acciones; repetible. Uno de `block`, `log`, `challenge`, `allow`. |
| `not_ip` | consulta | string | No | Excluye esas IP; repetible. |
| `not_host` | consulta | string | No | Excluye esos nombres; repetible. De 1 a 253 caracteres. |
| `not_path` | consulta | string | No | Excluye esas rutas exactas; repetible. De 1 a 2048 caracteres. |
| `not_status` | consulta | integer | No | Excluye esos estados HTTP; repetible. De 100 a 599. |
| `not_rule_name` | consulta | string | No | Excluye esos rule_name; repetible. De 1 a 200 caracteres. |
| `q` | consulta | string | No | Texto libre (sin distinguir mayúsculas) en host, ruta, IP, método, rule_name, attack_type, msg, user agent, país, rule_id y status. Como mucho 200 caracteres. |

#### Respuesta

- `200`: Stream de Server-Sent Events. Cuerpo de tipo `text/event-stream`.

#### Errores

| Estado | Descripción |
|---|---|
| `400` | Petición mal formada (`bad_request`). |
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |
| `429` | Demasiadas peticiones (`rate_limited`); espera los segundos de `Retry-After`. |
| `503` | No disponible ahora (`unavailable`); reinténtalo más tarde. |

#### Ejemplo

No está en los SDK: abre el stream con `EventSource` o con cualquier cliente de Server-Sent Events.

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