Run and monitor Claude Code sessions from anywhere. Connect a runner to a machine, then launch sessions, tail their output, and send follow-ups over a clean REST API. Every endpoint is org-scoped and returns JSON.
A runner is a small Node daemon you run on any machine you want to launch sessions on. It only makes outbound calls (pull model), so there are no inbound firewall holes. Create an API key under Settings → API Keys, then start the daemon:
$env:AGENTFLEET_URL="https://your-host"
$env:AGENTFLEET_API_KEY="af_live_..." # from Settings → API Keys
$env:ALLOWED_DIRS="C:\Users\you\code" # only these dirs are runnable
$env:RUNNER_NAME="home-desktop"
node runner/agentfleet-runner.mjsOn macOS / Linux use export for each variable; ALLOWED_DIRS is colon- or comma-separated. The runner enforces the directory allowlist: any working directory outside it is rejected. Once it heartbeats, it shows up under Runners in the app.
Generate an API key in the app under Settings → API Keys. Keys look like af_live_AbC123... and are shown once at creation — store it somewhere safe. Pass it as a Bearer token on every request:
Authorization: Bearer af_live_AbC123...Missing or invalid credentials return 401 with an error envelope:
{ "error": { "code": "UNAUTHORIZED", "message": "Authentication required" } }All endpoints live under /api/v1. Errors use the shape { "error": { "code", "message" } }.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/runners | List your runners |
| GET | /api/v1/runners/:id | Runner detail + sessions |
| POST | /api/v1/runs | Launch a session (enqueues) |
| GET | /api/v1/runs/:id | Get a session incl. logs |
| GET | /api/v1/runs/:id/events | Stream session output |
| POST | /api/v1/runs/:id/followup | Resume with a follow-up |
Launch a Claude Code session by POSTing a prompt and a cwd. This enqueues a task (status QUEUED) — a runner claims it and executes asynchronously. Optionally pin a runnerId, permissionMode (default, acceptEdits, bypassPermissions, plan), or model.
curl -X POST https://your-host/api/v1/runs \
-H "Authorization: Bearer af_live_..." \
-H "Content-Type: application/json" \
-d '{"prompt":"add a health check route","cwd":"C:\\Users\\you\\code\\api","permissionMode":"acceptEdits"}'{
"id": "clr...",
"status": "QUEUED",
"input": "add a health check route",
"cwd": "C:\\Users\\you\\code\\api",
"permissionMode": "acceptEdits",
"runnerId": "run_..."
}Fetch a session (including its logs and final cost / token usage) with GET /api/v1/runs/:id.
Stream a session's output incrementally. Poll GET /api/v1/runs/:id/events with ?after=<seq> to fetch only events newer than the last sequence number you saw.
curl "https://your-host/api/v1/runs/clr.../events?after=0" \
-H "Authorization: Bearer af_live_..."{ "data": [
{ "seq": 1, "kind": "system", "message": "session started" },
{ "seq": 2, "kind": "assistant", "message": "Adding the route…" },
{ "seq": 3, "kind": "tool_use", "message": "Edit src/app.ts" },
{ "seq": 4, "kind": "result", "message": "done" }
] }Continue a session by resuming it with a new prompt. POST to /api/v1/runs/:id/followup — this creates a new child session that resumes the parent's Claude session, inheriting its runner, cwd, permission mode, and model.
curl -X POST https://your-host/api/v1/runs/clr.../followup \
-H "Authorization: Bearer af_live_..." \
-H "Content-Type: application/json" \
-d '{"prompt":"now add a test for it"}'{ "id": "clr2...", "status": "QUEUED", "parentRunId": "clr..." }