# Referencia de la API

> Rutas y JSON de la API de gestión de OpenWAF Tasks en dash.openwaf.dev, las órdenes de la CLI y las herramientas MCP.

## API

Todas las rutas cuelgan de `https://dash.openwaf.dev/v1/orgs/{org}` y aceptan un token de la API de OpenWAF (`owf_…`) o la sesión del panel. Las lecturas necesitan `readonly`; las escrituras, un `admin` de toda la organización, llevan `X-Change-Reason` (queda en el registro de auditoría) y aceptan `?dry_run=true` para validar sin guardar.

| Ruta | Qué hace |
|---|---|
| `GET /tasks` | Resumen: `enabled`, `requirements`, `quota`, `usage`, `kill` y todas las tareas. Se puede leer antes de aprobar el acceso. |
| `POST /tasks` | Crea una tarea; `201 {"task", "webhook_secret"}` (el secreto solo si hay webhook, una vez) |
| `GET /tasks/{task}` | Lee una tarea (`{task}` es su id o su nombre) |
| `PATCH /tasks/{task}` | Cambia cualquier campo editable; `"rotate_webhook_secret": true` genera un secreto nuevo |
| `DELETE /tasks/{task}` | La borra (`204`) |
| `POST /tasks/{task}/run` | La ejecuta ahora; `202` con la ejecución |
| `GET /tasks/{task}/runs?limit=&before=` | Ejecuciones, de la más reciente a la más antigua: `{"items", "next"}` |
| `GET /tasks/browser-profiles?key_id=` | Perfiles de una clave de navegador: `{"items": [{"id", "name"}]}` |
| `GET /task-runs/{run}` | Una ejecución con sus `approvals` y `artifacts` |
| `GET /task-runs/{run}/logs?after=<seq>` | Líneas de registro tras `seq`: `{"items": [{"seq", "at", "level", "type", "message"}]}` |
| `GET /task-runs/{run}/artifacts/{id}` | Una captura (`image/jpeg` o `image/png`) |
| `GET /task-runs/{run}/recording` | `302` al fichero de la grabación, si la ejecución grabó |
| `POST /task-runs/{run}/cancel` | Cancela una ejecución; responde con la ejecución |
| `GET /task-approvals?status=&task=` | Aprobaciones: `{"items"}`; `status` es `pending`, `approved`, `rejected`, `expired`, `executed` o `failed` |
| `GET /task-approvals/{id}` | Una aprobación |
| `POST /task-approvals/{id}/approve` | `{"payload_sha256", "reason"}`; responde con la aprobación |
| `POST /task-approvals/{id}/reject` | `{"reason"}`; responde con la aprobación |

### Resumen

```json
{"enabled": true,
 "requirements": {"browsers": true, "egress": true, "email_verified": true, "two_factor": true},
 "quota": {"runs_per_month": 200, "max_tasks": 5, "max_concurrent": 1},
 "usage": {"period_start": "2026-10-01T00:00:00Z", "period_end": "2026-11-01T00:00:00Z",
           "runs": 14, "active": 0, "pending_approvals": 1},
 "kill": false,
 "tasks": [ … ]}
```

`kill` es `true` mientras el servicio está en pausa para todas las organizaciones: no empieza ninguna ejecución.

### Tarea

```json
{"id": "…", "name": "guestbook", "description": "", "kind": "script",
 "schedule": {"cron": "0 9 * * *", "timezone": "Europe/Madrid"},
 "paused": false,
 "browser": {"key_id": "…", "key_name": "ci-agent", "profile": "", "recording": false},
 "allow": ["example.com"],
 "script": "export default async function ({ page, task }) { … }",
 "input": {}, "state": {},
 "max_run_minutes": 10,
 "approval": {"policy": "required", "expire_minutes": 120, "relaxed_domains": []},
 "action_limits": [{"domain": "example.com", "min_interval_minutes": 30, "max_per_day": 4}],
 "webhook": {"url": "https://hooks.example.com/openwaf", "secret_set": true},
 "pool": "browsers", "egress_agent": null,
 "next_run_at": "2026-10-30T08:00:00Z",
 "last_run": {"id": "…", "status": "succeeded", "finished_at": "2026-10-29T08:00:41Z"},
 "created_at": "…", "updated_at": "…"}
```

| Campo | Reglas |
|---|---|
| `name` | Minúsculas, dígitos y guiones, hasta 63, único en la organización |
| `kind` | `script` o `agent`; no se puede cambiar |
| `schedule` | `{"cron", "timezone"}` (5 campos, zona IANA, como mucho cada 15 minutos) o `{"at"}` para una sola ejecución |
| `browser` | `key_id` de una clave de navegador de la organización, nombre de `profile` (vacío para un navegador limpio), `recording` |
| `allow` | De 1 a 20 dominios: `example.com` o `*.example.com` |
| `script` | Solo `script`: módulo ES de hasta 256 KiB. Mira el [inicio rápido del script](https://developers.openwaf.dev/es/tasks/quickstart-script/#write-the-script). |
| `agent` | Solo `agent`: `{"goal", "egress_target", "model", "max_steps"}`. Mira el [inicio rápido del agente](https://developers.openwaf.dev/es/tasks/quickstart-agent/#create-the-task). |
| `input` | JSON de hasta 64 KiB que reciben el script o el agente |
| `max_run_minutes` | De 1 a 30 |
| `approval` | `expire_minutes` de 10 a 1440. `policy` y `relaxed_domains` son de solo lectura; mira [políticas](https://developers.openwaf.dev/es/tasks/approvals/#policies). |
| `action_limits` | [Límites por dominio](https://developers.openwaf.dev/es/tasks/limits/#per-domain) más estrictos |
| `webhook` | `{"url"}` para fijarlo, `null` para quitarlo. Mira [webhook](https://developers.openwaf.dev/es/tasks/approvals/#webhook). |

`state`, `pool`, `egress_agent`, `next_run_at` y `last_run` son de solo lectura.

### Ejecución

```json
{"id": "…", "task_id": "…", "task_name": "guestbook", "number": 12, "trigger": "schedule",
 "status": "awaiting_approval",
 "created_at": "…", "started_at": "…", "finished_at": null, "error": "",
 "output": {"posted": "pending approval"},
 "steps": 0, "browser_seconds": 38, "model_calls": 0, "prompt_tokens": 0, "completion_tokens": 0,
 "blocked_requests": 0, "live_url": null, "recording_available": false, "egress_agent": null,
 "approvals": [ … ],
 "artifacts": [{"id": "…", "name": "before", "content_type": "image/jpeg", "size": 81234, "created_at": "…"}]}
```

`status` es uno de `queued`, `running`, `awaiting_approval`, `executing`, `succeeded`, `failed`, `rejected`, `expired`, `timed_out` y `canceled`. `trigger` es `schedule` o `manual`. `live_url` solo tiene valor mientras la ejecución tiene navegador.

### Aprobación

```json
{"id": "…", "task_id": "…", "task_name": "guestbook", "run_id": "…", "status": "pending",
 "summary": "Sign the guestbook with \"Hello from Example Inc.\"",
 "target": "https://example.com/guestbook", "domain": "example.com",
 "payload": {"steps": [{"op": "goto", "url": "https://example.com/guestbook"}, … ]},
 "payload_sha256": "9c1e…", "screenshot_id": "…",
 "requested_at": "…", "expires_at": "…", "decided_at": null, "decided_by": null, "reason": null,
 "not_before": null, "executed_at": null, "result": null}
```

Tras la fase de actuar, `result` es `{"ok", "final_url", "error", "screenshot_id"}`. Los pasos se describen en [aprobaciones](https://developers.openwaf.dev/es/tasks/approvals/#steps).

### Errores

| Estado | `code` | Cuándo |
|---|---|---|
| 403 | `feature_disabled` | OpenWAF Tasks no está activado para la organización |
| 403 | `forbidden` | El rol no basta, o el miembro o el token están limitados a un cliente o una zona |
| 404 | `not_found` | No existe esa tarea, ejecución, artefacto o aprobación |
| 409 | `payload_changed` | El `payload_sha256` enviado no es el de la aprobación |
| 409 | `not_pending` | La aprobación ya se decidió o caducó |
| 422 | `invalid` | Un campo incumple las reglas; `field` dice cuál |

## CLI

`openwaf-cloud ctl tasks …` llama a la API anterior. Mira [primeros pasos con la CLI](https://developers.openwaf.dev/es/cli/get-started/).

La organización sale de `--org` o de `OPENWAF_ORG`. Los cambios enseñan lo que harán y preguntan antes de aplicarse (`--yes` no pregunta, `--dry-run` solo valida).

| Orden | Qué hace |
|---|---|
| `tasks list` | Resumen y tareas |
| `tasks get <task>` | Una tarea |
| `tasks create <task.json\|-> --reason "…"` | Crea una tarea a partir de un cuerpo JSON |
| `tasks update <task> <patch.json\|-> --reason "…"` | Cambia una tarea |
| `tasks delete <task> --reason "…"` | Borra una tarea |
| `tasks run <task> --reason "…"` | La ejecuta ahora |
| `tasks runs <task> [--limit n]` | Ejecuciones de una tarea |
| `tasks run-get <run>` | Una ejecución con sus aprobaciones y artefactos |
| `tasks cancel <run> --reason "…"` | Cancela una ejecución |
| `tasks approvals [--status pending] [--task <task>]` | Aprobaciones |
| `tasks approve <approval> --sha256 <hash> --reason "…"` | Aprueba la carga exacta |
| `tasks reject <approval> --reason "…"` | La rechaza |

## Herramientas MCP

El [servidor MCP de OpenWAF](https://developers.openwaf.dev/es/mcp/) tiene 12 herramientas para OpenWAF Tasks. Las escrituras necesitan un `reason` y van en dos pasos: `confirm=false` solo comprueba y `confirm=true` aplica.

| Herramienta | Rol |
|---|---|
| `tasks_list` | `readonly` |
| `tasks_get` | `readonly` |
| `task_runs_list` | `readonly` |
| `task_runs_get` | `readonly` |
| `task_approvals_list` | `readonly` |
| `tasks_create` | `admin` |
| `tasks_update` | `admin` |
| `tasks_delete` | `admin` |
| `tasks_run` | `admin` |
| `task_runs_cancel` | `admin` |
| `task_approve` | `admin`. El paso de comprobación enseña la carga exacta, su `payload_sha256` y el id de la captura; el de aplicar necesita ese `payload_sha256`. |
| `task_reject` | `admin` |

Un asistente puede prepararte una aprobación, pero lee tú los pasos antes de confirmarla.
