OpenWAF Browsers tiene dos API:
- El plano de datos en
https://browsers.openwaf.dev, al que tu agente llama con una clave de navegador (bdk_…) para arrancar, listar y parar navegadores y para gestionar perfiles. - La API de gestión en
https://dash.openwaf.dev/v1/orgs/{org}/browsers, a la que llamas con un token de la API de OpenWAF (owf_…) o la sesión del panel para gestionar claves, ajustes, uso, sesiones y el registro de salida. La usan la CLI y las herramientas MCP.
Plano de datos
URL base https://browsers.openwaf.dev. Cada ruta necesita una clave de navegador, en cualquiera de estas cabeceras:
X-API-Key: bdk_…
Authorization: Bearer bdk_…JSON de entrada y de salida. Una clave solo ve los navegadores que arrancó ella.
Arrancar un navegador
POST /v1/browsers. Todos los campos son opcionales.
| Campo | Tipo | Significado |
|---|---|---|
profile | string | Id o nombre del perfil que se carga. Un perfil lo usa un navegador cada vez. |
metadata | object | Hasta 10 pares clave: valor de texto, para encontrar el navegador después (GET /v1/browsers?metadata=k=v). |
keepAlive | bool | No parar por inactividad: el navegador sigue hasta que lo paras o llega a la duración máxima de sesión. |
timeout | integer | Tiempo de inactividad en segundos: el navegador se para tras ese tiempo sin ningún cliente CDP ni de vista en directo. Por defecto 120. Manda sobre keepAlive. |
recording | bool | Graba la sesión en MP4. El archivo aparece en archivos cuando el navegador se para. |
viewport | object | {"width": 1280, "height": 800} |
lang | string | Idioma del navegador, por ejemplo en-US o es-ES. |
webBotAuth | bool | Firma las peticiones con Web Bot Auth. Por defecto, el ajuste de la organización: true salvo que un administrador lo haya desactivado. |
La respuesta es el navegador:
{"id": "bs_…", "status": "running",
"cdpUrl": "wss://browsers.openwaf.dev/cdp/…",
"cdpHttpUrl": "https://browsers.openwaf.dev/…",
"liveUrl": "https://browsers.openwaf.dev/…",
"urlsExpireAt": "2026-10-29T11:00:00Z", …}cdpUrles el WebSocket de DevTools del navegador:chromium.connectOverCDP(cdpUrl)en Playwright,puppeteer.connect({browserWSEndpoint: cdpUrl})en Puppeteer,npx @playwright/mcp@latest --cdp-endpoint <cdpUrl>para MCP.cdpHttpUrlsirve los endpoints HTTP de DevTools (/json/version,/json/list, …) con sus URL de WebSocket apuntando de vuelta abrowsers.openwaf.dev.liveUrlabre la pantalla en un navegador web, para mirar o tomar el control.- Los tokens de estas URL caducan en
urlsExpireAt, 1 hora después de emitirse.GET /v1/browsers/{id}devuelve otros nuevos mientras el navegador sigue en marcha.
Listar y leer navegadores
GET /v1/browserslista los navegadores de la clave. Filtros:status=running,metadata=k=v(se puede repetir).GET /v1/browsers/{id}devuelve un navegador, con URL nuevas mientras sigue en marcha.
Parar un navegador
PATCH /v1/browsers/{id}
Content-Type: application/json
{"action": "stop"}Al pararlo se cierra la grabación y se guarda y libera el perfil. Un navegador también se para solo:
- tras su tiempo de inactividad (120 s por defecto) sin ningún cliente CDP ni de vista en directo;
- al llegar a la duración máxima de sesión (60 minutos en la beta);
- cuando Chrome termina;
- cuando se rota o revoca su clave, o la organización agota la cuota.
Archivos
GET /v1/browsers/{id}/files lista las descargas y la grabación de un navegador, cada una con un enlace firmado que caduca. Los archivos de sesión se guardan 7 días.
Registro de salida
GET /v1/browsers/{id}/egress devuelve {"items": […]} con las últimas peticiones que hizo el navegador por el proxy de salida: hora, método, host, puerto, decision (allow, deny o error), el motivo de una denegación, estado y bytes. El registro de toda la organización está en la API de gestión.
Perfiles
Un perfil guarda cookies, sesiones iniciadas y almacenamiento local entre navegadores.
| Ruta | Qué hace |
|---|---|
POST /v1/profiles {"name": "…"} | Crea un perfil |
GET /v1/profiles | Lista los perfiles |
GET /v1/profiles/{id o nombre} | Lee un perfil |
PATCH /v1/profiles/{id o nombre} | Lo cambia |
DELETE /v1/profiles/{id o nombre} | Lo borra; 409 mientras lo usa un navegador |
Errores del plano de datos
Los errores son JSON: {"error": "…", "code": "…"}.
| Estado | code | Cuándo |
|---|---|---|
| 400 | Un campo no es válido; error dice cuál | |
| 401 | Clave ausente, desconocida, rotada o revocada, o la clave está desactivada (organización por encima de la cuota o sin activar) | |
| 404 | No existe ese navegador o perfil para esta clave | |
| 409 | Otro navegador está usando el perfil | |
| 429 | capacity_busy | Todos los huecos de navegador de la beta están ocupados. Espera lo que diga Retry-After (30 segundos) y reintenta. |
| 429 | key_concurrency | Esta clave ya tiene su máximo de navegadores en marcha (2). Para uno antes. |
| 503 | unavailable | El servicio está en pausa por mantenimiento. Reintenta más tarde. |
capacity_busy es capacidad compartida, no tu cuota: reintentar tras Retry-After es lo correcto. key_concurrency no se arregla esperando, salvo que se pare uno de tus navegadores.
API de gestión
Todas las rutas cuelgan de https://dash.openwaf.dev/v1/orgs/{org}, con un token de la API (Authorization: Bearer owf_…) o la sesión del panel. Para leer basta cualquier rol de toda la organización (readonly o superior). Las claves y los ajustes necesitan admin. Parar una sesión necesita tech o superior. Las escrituras llevan X-Change-Reason y quedan en el registro de auditoría; no son configuración versionada y no aceptan dry_run.
| Ruta | Qué hace |
|---|---|
GET /browsers | Resumen: estado, cuota, ajustes, uso del mes, capacidad y requisitos de acceso. Se puede leer antes de que se apruebe el acceso. |
PATCH /browsers/settings | Cambia egress_mode, allow y web_bot_auth (cualquier subconjunto); responde con el resumen |
GET /browsers/keys | Lista las claves |
POST /browsers/keys | Crea una clave: {"name": "ci-agent"}; responde 201 {"key", "secret"} |
POST /browsers/keys/{id}/rotate | Secreto nuevo: {"key", "secret"}. El anterior deja de valer al momento y sus navegadores se paran. |
DELETE /browsers/keys/{id} | Revoca una clave (204); sus navegadores se paran |
GET /browsers/usage | Series de uso, por clave y totales |
GET /browsers/sessions | Navegadores de la organización; ?status=running |
POST /browsers/sessions/{id}/stop | Para un navegador; responde con la sesión |
GET /browsers/egress | Registro de salida de la organización |
Resumen
{"enabled": true, "status": "enabled", "available": true,
"api_url": "https://browsers.openwaf.dev", "plan": "beta",
"quota": {"browser_hours": 20, "egress_gb": 5, "concurrency": 2, "max_session_minutes": 60, "max_keys": 5},
"settings": {"egress_mode": "open", "allow": [], "web_bot_auth": true},
"period": {"start": "2026-10-01T00:00:00Z", "end": "2026-11-01T00:00:00Z"},
"usage": {"browser_seconds": 4310, "egress_bytes": 123456789, "sessions": 31, "denied_requests": 4},
"usage_updated_at": "2026-10-29T12:01:00Z",
"warning": false,
"capacity": {"max_sessions": 5, "running": 2, "busy": false},
"requirements": {"owners_email_verified": true, "owners_two_factor": true}}statusesenabled,not_enabledoquota_exceeded.warningestruecuando las horas de navegador o la salida llegan al 80 % de la cuota.capacityes la capacidad compartida del servicio, onullsi no se conoce.requirementsdice si cada propietario y cada administrador tiene el email verificado y la verificación en dos pasos, que la aprobación exige.
Ajustes
{"egress_mode": "allowlist", "allow": ["example.com", "*.example.com"], "web_bot_auth": true}| Campo | Valores |
|---|---|
egress_mode | open: cualquier host público (por defecto). allowlist: solo los hosts de allow. Las direcciones privadas y de uso especial se rechazan en los dos modos. |
allow | Hasta 100 entradas. example.com es solo ese host; *.example.com, sus subdominios. |
web_bot_auth | Valor por defecto para los navegadores nuevos. Un navegador puede fijar webBotAuth en cualquier sentido. |
Claves
{"id": "6f1c…", "name": "ci-agent", "prefix": "bdk_AbC1", "disabled": false,
"created_at": "2026-10-29T10:00:00Z", "created_by": "…", "rotated_at": null}El nombre cumple ^[a-z0-9][a-z0-9-]{0,62}$ y es único en la organización. disabled es true mientras la organización supera la cuota o no está activada. El secret de una respuesta de creación o rotación se enseña una sola vez, con Cache-Control: no-store.
Uso
GET /browsers/usage?from=RFC3339&to=RFC3339&step=day|hour. Sin parámetros: el mes en curso, por día.
{"from": "…", "to": "…", "step": "day",
"series": [{"t": "2026-10-29T00:00:00Z", "browser_seconds": 1800, "egress_bytes": 52428800, "sessions": 6, "denied_requests": 1}],
"by_key": [{"key_id": "6f1c…", "name": "ci-agent", "browser_seconds": 1800, "egress_bytes": 52428800, "sessions": 6, "denied_requests": 1}],
"totals": {"browser_seconds": 1800, "egress_bytes": 52428800, "sessions": 6, "denied_requests": 1},
"quota": {…}, "period": {"start": "…", "end": "…"}, "usage": {…}}usage es lo que va de mes, la cifra con la que se compara la cuota. El uso se recoge cada minuto, así que las cifras van con hasta unos dos minutos de retraso.
Sesiones
GET /browsers/sessions?status=running:
{"items": [{"id": "bs_…", "key_id": "6f1c…", "key_name": "ci-agent", "status": "running",
"created_at": "…", "started_at": "…", "stopped_at": null, "stop_reason": null,
"live_url": "https://browsers.openwaf.dev/…", "metadata": {"task": "quickstart"}}]}status es running, idle o stopped. live_url es un enlace firmado de solo lectura, presente solo mientras el navegador está en marcha y válido 1 hora.
Registro de salida
GET /browsers/egress?key=<id>&since=RFC3339&decision=deny&limit=200. Del más reciente al más antiguo; limit hasta 1000, por defecto 200.
{"items": [{"ts": "…", "key_id": "6f1c…", "key_name": "ci-agent", "session": "bs_…",
"method": "CONNECT", "host": "example.com", "port": 443, "decision": "allow", "reason": "",
"status": 200, "bytes_up": 1830, "bytes_down": 48211, "ms": 412}]}decision es allow, deny o error; reason dice por qué se denegó una petición.
Errores de la API de gestión
| Estado | code | Cuándo |
|---|---|---|
| 403 | feature_disabled | OpenWAF Browsers no está activado para 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 existe esa clave o sesión en la organización |
| 409 | conflict | Ya existe una clave con ese nombre |
| 409 | key_limit | La organización ya tiene su máximo de claves (5 por defecto); revoca una antes |
| 422 | invalid | Un campo incumple las reglas de arriba; field dice cuál |
| 503 | unavailable | Ahora mismo no se llega al servicio de navegadores |
Acceso por operador
La aprobación la hace un operador, no es una ruta para clientes: un operador activa OpenWAF Browsers para la organización con un motivo y, si hace falta, otra cuota. Falla mientras algún propietario o administrador no tenga el email verificado o la verificación en dos pasos. Para pedirlo, mira pedir acceso.
CLI
openwaf-cloud ctl browsers … llama a la API de gestión. Cada cambio lleva --reason "…", enseña lo que va a hacer y pregunta antes de aplicarlo. --yes aplica sin preguntar (obligatorio sin terminal, como en CI); --dry-run solo lo enseña. Las lecturas aceptan --json para la respuesta de la API tal cual. Mira empezar con la CLI y cambios.
| Comando | Qué hace |
|---|---|
browsers status | El resumen: aprobación, uso del mes frente a la cuota, límites por clave, ajustes y capacidad compartida |
browsers settings [--egress-mode open|allowlist] [--allow a.example,*.example] [--web-bot-auth on|off] --reason "…" [--yes] | Cambia los ajustes (admin). --allow sustituye la lista entera; vacío es ninguno. Sin opciones enseña el resumen. |
browsers keys [list] | Claves, con prefijo y estado |
browsers keys create <nombre> --reason "…" [--yes] | Crea una clave (admin); el secreto se enseña una sola vez |
browsers keys rotate <clave> --reason "…" [--yes] | Secreto nuevo (admin); el actual deja de valer al momento. <clave> es el nombre o el id. |
browsers keys revoke <clave> --reason "…" [--yes] | Revoca una clave (admin) |
browsers usage [--from RFC3339] [--to RFC3339] [--step day|hour] | Uso por día u hora y por clave. --from es por defecto el día 1 de este mes. |
browsers sessions [--status running|idle|stopped] | Navegadores de la organización |
browsers sessions stop <id> --reason "…" [--yes] | Para un navegador (tech) |
browsers egress [--key <clave>] [--since 1h|24h|7d|RFC3339] [--deny] [--limit N] | Peticiones de los navegadores por el proxy de salida. --deny deja solo las denegadas; --limit es 200 por defecto, hasta 1000. |
Herramientas MCP
El servidor MCP de OpenWAF tiene 10 herramientas para OpenWAF Browsers. Las escrituras necesitan un reason y siguen los dos pasos de las demás herramientas no versionadas: confirm=false solo comprueba, confirm=true aplica, sin expected_version.
| Herramienta | Argumentos | Rol |
|---|---|---|
browser_status | readonly | |
browser_settings | ninguno para leer; egress_mode, allow, web_bot_auth, reason, confirm para cambiar | admin para cambiar |
browser_keys_list | readonly | |
browser_keys_create | name, reason, confirm; el secreto se devuelve una sola vez | admin |
browser_keys_rotate | key, reason, confirm | admin |
browser_keys_revoke | key, reason, confirm | admin |
browser_sessions_list | status | readonly |
browser_sessions_stop | session, reason, confirm | tech |
browser_usage | from, to, step | readonly |
browser_egress_events | key, since, decision, limit | readonly |
Las herramientas MCP gestionan la organización. Para manejar un navegador desde un asistente, usa el servidor MCP de Playwright con el cdpUrl del navegador, como en el inicio rápido.