- Replace all localhost:9090 with FLEET_API_URL:PORT placeholder - Add Step 0: persist Fleet API URL to agent memory - Clarify heartbeat must be periodic loop (60s interval) - Add execution mode self-selection decision flow - Add persisting configuration section (URL, agent_id, token)
11 KiB
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 <http_pull_token>
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-Hub-Signature-256 (or X-Gitea-Signature / X-Forgejo-Signature) header containing sha256=<hex_hmac> of the request body using the configured webhook_secret.
X-Hub-Signature-256: sha256=abcdef...
Error Responses
All errors return JSON:
{ "error": "<human-readable message>" }
| 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
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
{
"agent_id": "worker-01",
"registry_token": "registry_a1b2c3d4..."
}
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
{
"agent_id": "worker-01",
"status": "online",
"last_heartbeat_at": "2025-01-15T10:30:00Z"
}
Errors: 404 if agent not found.
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
{
"agent_id": "worker-01",
"status": "offline",
"requeued_tasks": 3
}
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 objects.
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 objects. Ordered by created_at descending.
curl 'http://FLEET_API_URL:PORT/api/v1/tasks?status=running'
Get Task
GET /api/v1/tasks/{task_id}
Response: 200 OK — single Task object.
Errors: 404 if task not found.
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 object, or 204 No Content if no matching task.
Errors: 401 if token required and missing/invalid.
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.
Errors: 400 if task is not http_pull mode or transition is invalid. 404 if task not found.
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 object.
Response: 200 OK
{
"task_id": "org/repo#42",
"status": "completed"
}
Errors: 404 if task not found. 400 if task is not in a completable state.
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.
Errors: 400 if task status is not failed or agent_lost. 404 if task not found.
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 object.
Response: 200 OK
Errors: 404 if task not found. 400 if validation fails.
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
{
"accepted": true,
"task_id": "org/repo#42"
}
Errors: 401 if signature invalid. 400 if payload unparseable.
Object Schemas
Agent Object
{
"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
{
"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
{
"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