# Agent Fleet — HTTP API Reference `FLEET_API_URL:PORT` means the address of your Agent Fleet Orchestrator (for example, `100.102.101.43:9090`). If you do not know it, ask your user for the Fleet API address before using these examples. Base URL: `http://FLEET_API_URL:PORT` Content-Type: `application/json` for all request/response bodies unless noted. All timestamps are ISO 8601 (RFC 3339). If you do not know the Fleet API URL, ask the user who deployed the orchestrator. --- ## Authentication ### http_pull Bearer Token Endpoints that are specific to `http_pull` agents require a Bearer token in the `Authorization` header. The token is configured in `config.toml` as `orchestrator.http_pull_token`. If no token is configured in the config, authentication is skipped (open mode). ``` Authorization: Bearer ``` Affected endpoints: `POST /api/v1/tasks/dequeue`, `POST /api/v1/tasks/{task_id}/status`. ### Webhook HMAC-SHA256 The `POST /api/v1/webhooks/forgejo` endpoint requires an `X-Gitea-Signature` or `X-Forgejo-Signature` header containing `sha256=` of the request body using the configured `webhook_secret`. ``` X-Forgejo-Signature: sha256=abcdef... ``` --- ## Error Responses All errors return JSON: ```json { "error": "" } ``` | Status | Meaning | Trigger | |--------|---------|---------| | 400 | Bad Request | Invalid state transition, wrong execution_mode, malformed input | | 401 | Unauthorized | Missing or invalid Bearer token for http_pull endpoints | | 404 | Not Found | Task or agent does not exist | | 500 | Internal Server Error | Database failure, lock poisoning, unexpected errors | --- ## Endpoints ### Health Check ``` GET /healthz ``` **Response:** `200 OK` — body: `ok` ```bash curl http://FLEET_API_URL:PORT/healthz ``` --- ### Register Agent ``` POST /api/v1/agents/register ``` Register a new agent or update an existing one (upsert by `agent_id`). **Request:** | Field | Type | Required | Description | |-------|------|----------|-------------| | agent_id | string | yes | Unique identifier | | agent_type | string | yes | `openclaw`, `claude-code`, `codex-cli`, `hermes`, `acp`, `shell`, or custom | | hostname | string | yes | Machine hostname | | capabilities | string[] | yes | e.g. `["code:rust", "review"]` | | max_concurrency | u32 | yes | Max parallel tasks | | metadata | object | no | Arbitrary key-value pairs | **Response:** `200 OK` ```json { "agent_id": "worker-01", "registry_token": "registry_a1b2c3d4..." } ``` ```bash curl -X POST http://FLEET_API_URL:PORT/api/v1/agents/register \ -H 'Content-Type: application/json' \ -d '{ "agent_id": "worker-01", "agent_type": "codex-cli", "hostname": "host-worker-01", "capabilities": ["code:rust"], "max_concurrency": 2 }' ``` --- ### Heartbeat ``` POST /api/v1/agents/heartbeat ``` This endpoint MUST be called periodically (default: every 60s). A single call is not sufficient. Missing heartbeats will cause the agent to be marked offline and its tasks requeued. **Request:** | Field | Type | Required | Description | |-------|------|----------|-------------| | agent_id | string | yes | Agent to update | **Response:** `200 OK` ```json { "agent_id": "worker-01", "status": "online", "last_heartbeat_at": "2025-01-15T10:30:00Z" } ``` **Errors:** `404` if agent not found. ```bash curl -X POST http://FLEET_API_URL:PORT/api/v1/agents/heartbeat \ -H 'Content-Type: application/json' \ -d '{"agent_id": "worker-01"}' ``` --- ### Deregister Agent ``` POST /api/v1/agents/deregister ``` Sets agent offline and requeues all its active tasks back to `created`. **Request:** | Field | Type | Required | |-------|------|----------| | agent_id | string | yes | **Response:** `200 OK` ```json { "agent_id": "worker-01", "status": "offline", "requeued_tasks": 3 } ``` ```bash curl -X POST http://FLEET_API_URL:PORT/api/v1/agents/deregister \ -H 'Content-Type: application/json' \ -d '{"agent_id": "worker-01"}' ``` --- ### List Agents ``` GET /api/v1/agents ``` **Query Parameters:** | Param | Type | Description | |-------|------|-------------| | capability | string | Filter by capability (e.g. `code:rust`) | | status | string | Filter: `online`, `offline`, `draining` | **Response:** `200 OK` — JSON array of [Agent](#agent-object) objects. ```bash curl 'http://FLEET_API_URL:PORT/api/v1/agents?status=online' ``` --- ### List Tasks ``` GET /api/v1/tasks ``` **Query Parameters:** | Param | Type | Description | |-------|------|-------------| | status | string | Filter by status (e.g. `created`, `running`, `failed`) | | agent_id | string | Filter by assigned agent | **Response:** `200 OK` — JSON array of [Task](#task-object) objects. Ordered by `created_at` descending. ```bash curl 'http://FLEET_API_URL:PORT/api/v1/tasks?status=running' ``` --- ### Get Task ``` GET /api/v1/tasks/{task_id} ``` **Response:** `200 OK` — single [Task](#task-object) object. **Errors:** `404` if task not found. ```bash curl http://FLEET_API_URL:PORT/api/v1/tasks/org%2Frepo%2342 ``` --- ### Dequeue Task (http_pull only) ``` POST /api/v1/tasks/dequeue ``` Requires Bearer token if `http_pull_token` is configured. Only returns tasks with `execution_mode = http_pull`. **Request:** | Field | Type | Required | Description | |-------|------|----------|-------------| | agent_id | string | yes | Agent claiming the task | | capabilities | string[] | no | Capabilities to match against task labels | **Response:** `200 OK` with [Task](#task-object) object, or `204 No Content` if no matching task. **Errors:** `401` if token required and missing/invalid. ```bash curl -X POST http://FLEET_API_URL:PORT/api/v1/tasks/dequeue \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer my-token' \ -d '{"agent_id": "worker-03", "capabilities": ["code:rust"]}' ``` --- ### Update Task Status (http_pull only) ``` POST /api/v1/tasks/{task_id}/status ``` Requires Bearer token. Only works for tasks with `execution_mode = http_pull`. **Request:** | Field | Type | Required | Description | |-------|------|----------|-------------| | status | string | yes | Target status: `running`, `review_pending`, etc. | **Response:** `200 OK` — updated [Task](#task-object). **Errors:** `400` if task is not `http_pull` mode or transition is invalid. `404` if task not found. ```bash curl -X POST http://FLEET_API_URL:PORT/api/v1/tasks/org%2Frepo%2342/status \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer my-token' \ -d '{"status": "running"}' ``` --- ### Complete Task ``` POST /api/v1/tasks/{task_id}/complete ``` Works for both `ssh_cli` and `http_pull` tasks. Submit a receipt to mark the task done. **Request:** A [Receipt](#receipt-object) object. **Response:** `200 OK` ```json { "task_id": "org/repo#42", "status": "completed" } ``` **Errors:** `404` if task not found. `400` if task is not in a completable state. ```bash curl -X POST http://FLEET_API_URL:PORT/api/v1/tasks/org%2Frepo%2342/complete \ -H 'Content-Type: application/json' \ -d '{ "task_id": "org/repo#42", "agent_id": "worker-01", "status": "completed", "duration_seconds": 120, "summary": "Implemented feature X", "artifacts": [ {"artifact_type": "pr", "url": "https://git.example/org/repo/pulls/7"} ], "error": null }' ``` --- ### Retry Task ``` POST /api/v1/tasks/{task_id}/retry ``` Retry a `failed` or `agent_lost` task. Transitions it back to `assigned`. **Response:** `200 OK` — updated [Task](#task-object). **Errors:** `400` if task status is not `failed` or `agent_lost`. `404` if task not found. ```bash curl -X POST http://FLEET_API_URL:PORT/api/v1/tasks/org%2Frepo%2342/retry ``` --- ### Submit Receipt ``` POST /api/v1/receipts ``` Submit a receipt for a task. Validates artifacts (e.g. checks PR exists via Forgejo API). **Request:** A [Receipt](#receipt-object) object. **Response:** `200 OK` **Errors:** `404` if task not found. `400` if validation fails. ```bash curl -X POST http://FLEET_API_URL:PORT/api/v1/receipts \ -H 'Content-Type: application/json' \ -d '{ "task_id": "org/repo#42", "agent_id": "worker-01", "status": "completed", "duration_seconds": 95, "summary": "Fixed the bug", "artifacts": [], "error": null }' ``` --- ### Forgejo Webhook ``` POST /api/v1/webhooks/forgejo ``` Receives Forgejo webhook events. Requires HMAC-SHA256 signature header. **Headers:** `X-Forgejo-Event` or `X-Gitea-Event` determines the event type. **Supported events:** | Event | Action | |-------|--------| | `issues` (opened) | Creates a task from the Issue (requires `agent:*` label) | | `pull_request` (opened) | Sets task to `review_pending` (branch name → task_id) | | `pull_request` (merged/closed with `merged: true`) | Sets task to `completed`, auto-generates receipt | | `push` (to `task/*` branch) | Updates `last_activity_at` on the task | **Response:** `200 OK` ```json { "accepted": true, "task_id": "org/repo#42" } ``` **Errors:** `401` if signature invalid. `400` if payload unparseable. --- ## Object Schemas ### Agent Object ```json { "agent_id": "worker-01", "agent_type": "codex-cli", "hostname": "host-worker-01", "capabilities": ["code:rust"], "max_concurrency": 2, "current_tasks": 1, "status": "online", "last_heartbeat_at": "2025-01-15T10:30:00Z", "registered_at": "2025-01-15T09:00:00Z", "metadata": {} } ``` ### Task Object ```json { "task_id": "org/repo#42", "source": "forgejo:org/repo#42", "task_type": "code", "priority": "normal", "status": "created", "execution_mode": "ssh_cli", "assigned_agent_id": null, "assigned_host": null, "requirements": "Implement the feature described in the issue body", "labels": ["agent:code", "code:rust"], "branch_name": "task/org%2Frepo%2342", "pr_title": "feat: Implement feature (#42)", "created_at": "2025-01-15T10:00:00Z", "assigned_at": null, "started_at": null, "completed_at": null, "last_activity_at": null, "retry_count": 0, "max_retries": 2, "review_count": 0, "timeout_seconds": 1800 } ``` **Status values:** `created`, `assigned`, `running`, `review_pending`, `completed`, `failed`, `agent_lost`, `cancelled` **Priority values:** `low`, `normal`, `high`, `urgent` **Execution mode values:** `ssh_cli`, `http_pull` ### Receipt Object ```json { "task_id": "org/repo#42", "agent_id": "worker-01", "status": "completed", "duration_seconds": 120, "summary": "Implemented the feature", "artifacts": [ {"artifact_type": "pr", "url": "https://git.example/org/repo/pulls/7", "path": null, "description": null} ], "error": null } ``` **Receipt status values:** `completed`, `failed`, `partial` **Artifact type values:** `pr`, `commit`, `file`, `comment`, `url`