HTTP API
Authenticate with a bearer credential and control the fleet with HTTP and WebSocket.
The hub serves one HTTP API, on port 7080 by default. The iOS app, the web cockpit and your orchestrator use it. The API uses JSON, and WebSocket for live streams.
Use the API on your private network only. The listener does not provide TLS. Read Security and privacy.
Authentication
Send a bearer credential on each request:
curl -fsS \
-H "Authorization: Bearer $DROVER_BEARER" \
http://127.0.0.1:7080/harness/hosts
A request without a valid credential returns 401. The health checks, the login page, pairing-code redemption and the host join probe need no credential.
Credential types
| Credential | Source | Access |
|---|---|---|
| Device | A pairing code from drover-server pair |
The fleet API. The phone holds one. |
| Host | A pairing code from drover-server pair-host |
Host registration and relay. |
| Profile | drover profile agents issue AGENT |
Read-only. Only the profile. |
| Preflight | drover-server credentials issue-preflight |
Read-only. Health, release identity and the host list. |
| Shared token | ~/.drover/api_token |
All routes, and operator actions. |
The hub stores only a hash of each credential. To revoke one credential:
drover-server credentials list
drover-server credentials revoke <credential-id>
The shared token is on by default so that upgrades continue to work. After each device and host has its own credential, disable it:
# ~/.drover/config.toml
[auth]
legacy_token_enabled = false
For a script or an orchestrator, use a dedicated credential. A device or host credential gives shell access to each registered host.
Pair a client with the API
-
Create a pairing code with an authenticated request:
curl -fsS -X POST -H "Authorization: Bearer $DROVER_BEARER" \ -H 'Content-Type: application/json' \ http://127.0.0.1:7080/auth/pair-codes -d '{"scope": "device"}' -
Redeem the code on the new client:
curl -fsS -X POST -H 'Content-Type: application/json' \ http://127.0.0.1:7080/auth/pair \ -d '{"code": "XXXX-XXXX", "device_name": "my-orchestrator"}'
A code is single use. A device code expires after ten minutes. A host code expires after fifteen minutes.
Endpoints
{id} is a session id. {host_id} is a host id. This list is not complete, and no OpenAPI schema exists.
Health
| Method | Path | Purpose |
|---|---|---|
GET |
/healthz |
Liveness. No credential. |
GET |
/readyz |
Readiness. Detail needs a credential. |
Fleet and hosts
| Method | Path | Purpose |
|---|---|---|
GET |
/harness |
Hosts and their sessions. |
GET |
/harness/hosts |
Hosts, with connection type and available harnesses. |
GET |
/harness/hosts/{host_id}/model-catalog |
Models that the host reports. |
POST |
/harness/hosts/{host_id}/sessions |
Start a session. |
Sessions
| Method | Path | Purpose |
|---|---|---|
GET |
/harness/sessions |
List sessions. |
GET |
/harness/sessions/{id} |
Get one session. |
GET |
/harness/sessions/{id}/messages |
Get the transcript, in pages. |
POST |
/harness/sessions/{id}/turns |
Send a turn. |
POST |
/harness/sessions/{id}/permission |
Answer an approval request. |
POST |
/harness/sessions/{id}/interrupt |
Interrupt the current turn. |
POST |
/harness/sessions/{id}/continue |
Hand off to another host or harness. |
POST |
/harness/sessions/{id}/terminate |
Stop the session. |
GET POST |
/harness/sessions/{id}/publications |
Read or record the branch and pull request of a session. |
| WebSocket | /harness/sessions/{id}/stream |
Live structured events. |
| WebSocket | /harness/sessions/{id}/terminal |
Live terminal. |
Memory and analytics
| Method | Path | Purpose |
|---|---|---|
GET |
/profile |
The portable profile. |
GET |
/sessions/history |
Past sessions. |
GET |
/cockpit/overview |
Data for the cockpit home view. |
GET |
/projects/activity |
Activity for each project. |
GET |
/harness/lifecycle |
A report of idle sessions and worktrees. |
Run a session
-
Start a session. Set
modetostructuredfor chat control, or toptyfor a terminal:curl -fsS -X POST -H "Authorization: Bearer $DROVER_BEARER" \ -H 'Content-Type: application/json' \ http://127.0.0.1:7080/harness/hosts/studio/sessions \ -d '{ "mode": "structured", "harness": "codex", "cwd": "/path/to/project", "prompt": "Add rate limiting to the export endpoint." }'modelandthinking_effortare optional. Take their values from the model catalog of the host. -
Send a turn:
curl -fsS -X POST -H "Authorization: Bearer $DROVER_BEARER" \ -H 'Content-Type: application/json' \ http://127.0.0.1:7080/harness/sessions/SESSION_ID/turns \ -d '{"text": "Also cover the empty config case."}' -
Answer an approval. Use the request id from the event stream. Set
decisiontoallowordeny:curl -fsS -X POST -H "Authorization: Bearer $DROVER_BEARER" \ -H 'Content-Type: application/json' \ http://127.0.0.1:7080/harness/sessions/SESSION_ID/permission \ -d '{"request_id": "REQUEST_ID", "decision": "allow"}'Only Claude Code sessions support approvals.
-
Hand off the session to another host or harness:
curl -fsS -X POST -H "Authorization: Bearer $DROVER_BEARER" \ -H 'Content-Type: application/json' \ http://127.0.0.1:7080/harness/sessions/SESSION_ID/continue \ -d '{"target_host_id": "laptop", "target_harness": "claude-code"}'The two fields default to the values of the source session. The new session starts in the same directory path, so the project must be at that path on the target host.
Stop a session
POST /harness/sessions/{id}/terminate records your stop request, then contacts the host.
- If the host confirms, the response is the normal success response.
- If the host is offline or does not confirm, the response is
202with"state": "pending".
A 202 means that the hub accepted the request. It does not mean that the process stopped. The hub confirms or tries again when the host connects.
Errors
| Status | Meaning |
|---|---|
400 |
The input is not valid, or the harness cannot do the operation. |
401 |
The credential is missing or not valid. |
403 |
The scope of the credential does not permit this route. |
503 |
An analytical route is busy or unavailable. Wait for Retry-After, then try again. |
Analytical reads have a five-second deadline. They return an unavailable response, not partial data.
This page is a summary. The reference is docs/security.md.