# Referencia del egress alojado

> Rutas, roles, límites, el catálogo de proveedores, los errores de la API y las respuestas de la pasarela de egress de IA alojada en OpenWAF Cloud.

La pasarela alojada es la de código abierto, [OpenWAF Egress](https://developers.openwaf.dev/es/egress/), ejecutada por OpenWAF Cloud en `https://egress.openwaf.dev`, con los agentes, los destinos y las claves gestionados por organización en lugar de con un fichero de configuración.

## URL de la pasarela

```text
https://egress.openwaf.dev/v1/agent/<nombre del agente>/proxy/<id del destino>/<ruta del proveedor>
Authorization: Bearer OWE_TOKEN_…
```

`<ruta del proveedor>` y la cadena de consulta se añaden a la URL base del destino: `…/proxy/openrouter/chat/completions` va a `https://openrouter.ai/api/v1/chat/completions`. Las `base_urls` de cada agente dan la URL de cada destino al que puede llamar.

Frente a una pasarela en tu equipo:

- El agente se identifica por su token, y la URL tiene que nombrar al agente de ese token; ni `/v1/proxy/<destino>/…` ni la cabecera `X-OpenWAF-Agent` se usan.
- El token del agente nunca se reenvía al proveedor; en su lugar, la cabecera de autenticación del destino lleva la clave del proveedor.
- Un destino sin clave de proveedor se rechaza en lugar de llamarlo sin clave.
- Además de la comprobación previa, la dirección a la que se conecta de verdad se vuelve a comprobar contra los rangos privados y reservados.
- Los cambios de agentes, destinos y claves llegan a la pasarela en segundos; una clave de proveedor se guarda en caché hasta cinco minutos.

Las comprobaciones, los detectores, los límites del cuerpo y los plazos son los de la pasarela de código abierto: mira el [modelo de seguridad](https://developers.openwaf.dev/es/egress/security-model/) y los [límites fijos](https://developers.openwaf.dev/es/egress/configuration/#fixed-limits).

## Errores de la pasarela

Los errores de la propia pasarela son JSON con un código en `error` y un `message` (en inglés); las respuestas del proveedor llegan sin cambios.

| Estado | `error` | Causa |
|---|---|---|
| 401 | `token_required` | No hay cabecera `Authorization` |
| 401 | `invalid_token` | Token desconocido o rotado, o el agente está desactivado o borrado |
| 401 | `unauthorized_agent` | La URL no nombra al agente del token |
| 403 | `egress_blocked` | El destino no está entre los permitidos del agente (otro agente de la organización sí puede llamarlo) |
| 403 | `ssrf_blocked` | El destino resuelve a una dirección privada o reservada |
| 403 | `injection_detected` | Un detector de inyección de prompt ha saltado con el cuerpo (**Bloquear la inyección de prompts** activado) |
| 403 | `data_leak_blocked` | Un patrón de fuga de datos ha saltado con el cuerpo (**Bloquear fugas de datos** activado) |
| 404 | `unknown_target` | Ningún agente de la organización puede llamar a ese id de destino, o no existe |
| 413 | `body_too_large` | Más de 8 MiB, o más de 1 MiB de texto que inspeccionar |
| 502 | `credential_resolution_failed` | El destino no tiene clave de proveedor, o no se pudo leer |
| 502 | `upstream_error` | No se pudo llegar al proveedor |
| 503 | `policy_unavailable` | La pasarela no tiene una política vigente para la petición; reinténtalo más tarde |

Las respuestas `403` y `413` aparecen en [peticiones bloqueadas](https://developers.openwaf.dev/es/egress/cloud/blocked-requests/). La [página de solución de problemas](https://developers.openwaf.dev/es/egress/troubleshooting/) explica cada código con detalle.

## API de gestión

Todas las rutas cuelgan de `https://dash.openwaf.dev/v1/orgs/{org}`, con un token de API (`Authorization: Bearer owf_…`) o la sesión del panel. Leer necesita cualquier rol de toda la organización; escribir, `admin` de toda la organización. Los miembros y tokens limitados a un cliente o a una zona reciben `403`. Las escrituras admiten un `X-Change-Reason` opcional; no son configuración versionada y rechazan `dry_run`. Los SDK de TypeScript y Python incluyen todas las rutas salvo el flujo.

| Ruta | Qué hace |
|---|---|
| `GET /egress` | `{enabled, gateway_url, credentials_available}`: si Egress IA está activado en la organización; se puede leer aunque no lo esté |
| `GET /egress/targets` | El catálogo y después los destinos propios, cada uno con los metadatos de su clave (`credential`: huella, `updated_at`, `updated_by`, o `null`) |
| `POST /egress/targets` | Añade un destino propio: `id`, `name`, `base_url`, `auth_header`, `auth_prefix` |
| `DELETE /egress/targets/{target}` | Borra un destino propio y su clave |
| `PUT /egress/targets/{target}/credential` | Pone o reemplaza la clave: `{"secret": "…"}`; responde el destino con la huella de la clave |
| `DELETE /egress/targets/{target}/credential` | Quita la clave |
| `GET /egress/agents`, `GET /egress/agents/{id}` | Agentes, con `base_urls` por destino permitido y `token_prefix` |
| `POST /egress/agents` | Crea un agente: `name`, `description`, `allowed_targets`, `enforce_injection`, `block_dlp`, `disabled`; responde `{agent, token}` |
| `PATCH /egress/agents/{id}` | Cambia `description`, `allowed_targets` (se sustituye entera), `enforce_injection`, `block_dlp` o `disabled` |
| `POST /egress/agents/{id}/rotate-token` | Token nuevo; responde `{agent, token}` |
| `DELETE /egress/agents/{id}` | Borra un agente |
| `GET /egress/events` | Peticiones bloqueadas, las más recientes primero, con los filtros de los eventos de seguridad |
| `GET /egress/events/stream` | Peticiones bloqueadas como Server-Sent Events |

Leer y borrar agentes, destinos y claves funciona aunque Egress IA esté desactivado en la organización; las demás escrituras, y los eventos, responden `403 feature_disabled`. Las respuestas que llevan un token o ponen una clave van con `Cache-Control: no-store`.

## Límites y formatos

| Elemento | Regla |
|---|---|
| Nombre del agente | `^[a-z0-9][a-z0-9-]{0,62}$`, único en la organización, no se puede cambiar |
| Descripción | Hasta 500 caracteres |
| Destinos permitidos | De 1 a 32 ids de destino por agente |
| Token | `OWE_TOKEN_` y 32 bytes aleatorios en base64url, se enseña una vez; solo se guardan su SHA-256 y su prefijo (`OWE_TOKEN_` y 6 caracteres) |
| Id de destino propio | `^[a-z][a-z0-9-]{1,31}$`, que no sea un id del catálogo |
| Nombre de destino propio | Hasta 100 caracteres |
| URL de destino propio | `https://`, puerto 443, nombre DNS público, sin IP, credenciales, query ni fragmento |
| Cabecera y prefijo | Un nombre de cabecera de hasta 64 caracteres; un prefijo de hasta 32 caracteres imprimibles |
| Clave de proveedor | De 1 a 8192 caracteres, sin espacios ni caracteres de control |

## Errores de la API

| Estado | `code` | Cuándo |
|---|---|---|
| 403 | `feature_disabled` | Egress IA no está activado en la organización |
| 403 | `forbidden` | El rol no basta, o el miembro o el token está limitado a un cliente o a una zona |
| 404 | `not_found` | No hay ese agente o destino en la organización |
| 409 | `conflict` | Ya existe un agente o un destino propio con ese nombre o id |
| 409 | `target_in_use` | Se intenta borrar un destino propio que algún agente aún permite |
| 422 | `invalid` | Un campo incumple las reglas de arriba (`field` lo nombra), o se intenta borrar un destino del catálogo |
| 502 | `credential_store` | El almacén de credenciales no aceptó el cambio; vuelve a intentarlo |
| 503 | `unavailable` | Ahora mismo no se pueden guardar claves de proveedor (`credentials_available` es `false`) |

## Catálogo

| Id | Proveedor | URL base | Cabecera de la clave |
|---|---|---|---|
| `openrouter` | OpenRouter | `https://openrouter.ai/api/v1` | `Authorization: Bearer` |
| `openai` | OpenAI | `https://api.openai.com/v1` | `Authorization: Bearer` |
| `anthropic` | Anthropic | `https://api.anthropic.com/v1` | `x-api-key` |
| `huggingface` | Hugging Face | `https://router.huggingface.co/v1` | `Authorization: Bearer` |
| `groq` | Groq | `https://api.groq.com/openai/v1` | `Authorization: Bearer` |
| `mistral` | Mistral AI | `https://api.mistral.ai/v1` | `Authorization: Bearer` |
| `gemini` | Google Gemini (compatible con OpenAI) | `https://generativelanguage.googleapis.com/v1beta/openai` | `Authorization: Bearer` |
| `deepseek` | DeepSeek | `https://api.deepseek.com/v1` | `Authorization: Bearer` |
| `together` | Together AI | `https://api.together.xyz/v1` | `Authorization: Bearer` |
| `fireworks` | Fireworks AI | `https://api.fireworks.ai/inference/v1` | `Authorization: Bearer` |
| `cohere` | Cohere | `https://api.cohere.com/v2` | `Authorization: Bearer` |
| `perplexity` | Perplexity | `https://api.perplexity.ai` | `Authorization: Bearer` |
| `xai` | xAI | `https://api.x.ai/v1` | `Authorization: Bearer` |

## Herramientas MCP

`egress_status` y `egress_events` leen; `egress_create_agent`, `egress_update_agent`, `egress_rotate_token`, `egress_delete_agent`, `egress_add_target`, `egress_remove_target` y `egress_remove_credential` escriben, con `confirm=false` para comprobar primero y sin `expected_version`. No hay herramienta para poner una clave de proveedor. Mira las [herramientas MCP](https://developers.openwaf.dev/es/mcp/tools/#write-tools).
