Skip to main content
Port 8080 belongs to the native control front (ados-control). It serves most routes itself and forwards a fixed set of prefixes to a residual Python app over an internal Unix socket, authenticating them first. To a client it is one HTTP API for status, telemetry, commands, configuration, video, pairing and the rest. The CLI, the terminal dashboard, the browser dashboard and Mission Control all use it. Treat :8080 as one API. Which process answers a route is not a contract.
/api/command, the parameter write routes and the MAVLink WebSocket act on the flight controller. Read Safety before scripting against a powered vehicle.

Base URL

For example http://192.168.1.50:8080 or http://ados-9f2c1a.local:8080. On the node itself use http://localhost:8080. The API is plain HTTP on the LAN; see Hardening.

Authentication

Send the pairing key in the X-ADOS-Key header. The key is created at pairing and stored in /etc/ados/pairing.json; it starts with ados_.
Three more rules apply at the auth edge:
  • Public routes. A few routes answer without a key so an unpaired node can be found and paired: /api/pairing/info, /api/pairing/claim, /api/pairing/code, /api/ping, /api/version, /healthz and the dashboard PIN routes. The full list is in Public routes.
  • The unpaired gate. While a node is unpaired, its data routes answer on-box callers and the first-boot lifelines (link-local, the node’s own hotspot at 192.168.4.1 and USB tether at 192.168.7.1), require a dashboard PIN session from other callers on the operator LAN, and refuse remote callers. The pairing claim is refused to remote callers.
  • Dashboard PIN session. The browser dashboard exchanges its PIN at POST /api/dashboard/pin/verify and then sends the session in the x-ados-dashboard-session header instead of a key. The dashboard PIN itself is covered on Agent dashboard overview.
A browser cannot set X-ADOS-Key on a WebSocket handshake. Mint a short-lived ticket with POST /api/_ws/ticket (authenticated with the key) and pass it as a subprotocol: new WebSocket(url, ["ados-ws-ticket", ticket]).

Conventions

  • JSON. Requests and responses are JSON unless a route says otherwise (for example GET /api/video/snapshot returns image/jpeg).
  • Armed guard. Actions that can drop the link or change how the vehicle flies (reboot, supervisor restart, link-carrying unit restarts, radio hop, TX power and unpair, profile or role changes, signing enrolment, parameter writes, extension install and lifecycle) answer 409 while the vehicle is armed:
    Override with "force": true in the JSON body, or ?force=1 on a bodiless request.
  • Residual routes. A residual prefix answers 501 rather than 404 on a profile that does not run that feature.
  • Relay-forbidden routes. Pairing, unpair, MCP token, dashboard PIN set, signing disable and some setup routes refuse requests that arrive through a ground station’s relay proxy. Call them on the node’s own network.
  • Extension errors. The extension lifecycle routes use one envelope: {"ok": false, "code": N, "kind": "...", "detail": "..."}.

Route domains

Examples

Status

Configuration

The write response carries persisted; see Configuration.

Video snapshot

Returns one JPEG grabbed live from the main path, with an X-Captured-At header. Nothing is written to disk. The route answers 503 with E_NO_FRAME when no frame is available.

Pairing

Ground station

Extension HTTP passthrough

An extension that declares agent.http: true serves its own API on a Unix socket the plugin host creates for it. /api/plugins/{id}/x/{path} forwards GET, POST, PUT, PATCH and DELETE there as /{path}, keeping the query string, and upgrades WebSockets. The key is checked before the request is forwarded, and the route also works through a ground station’s relay proxy. When the extension is not serving, the answer is 503 with {"detail": "plugin <id> is not serving HTTP"}.
An installed extension record carries failure_reason: why the host stopped or refused the extension (for example tampered: <detail> or signer_revoked), or null when it has not failed. GET /api/plugins/{id}/locales/{lang} returns the extension’s locales/<lang>.json bundle so Mission Control can show its labels in the extension’s own words; lang is a bare tag of letters, digits, - and _.

Video attention profile

/api/video/profile sets which video profile this drone streams. POST {"profile": "hero"} or {"profile": "thumbnail"} moves the base profile, which is what a ground station’s fleet-hero assignment sends. A viewer can instead hold a lease: POST {"profile": "hero", "holder": "<id>", "lease_s": 30} creates or renews it, and DELETE /api/video/profile?holder=<id> (or a thumbnail POST with the same holder) releases it. The drone streams hero while any lease is live, and a lease lapses lease_s seconds (5 to 120, default 30) after its last renewal, so a viewer that goes away cannot leave a drone on hero. holder is 1 to 64 characters of letters, digits, ., _, : and -; vision is reserved. Every method answers 200 with the profile the encoder actually applied and the live leases, or 503 on a node with no video pipeline.

CORS

CORS is on with an allowlist, not a wildcard. The defaults cover the local Mission Control development origins (localhost:4000, localhost:4001). Add origins without dropping the defaults:
The effective allowlist is the union of the built-in defaults, cors_origins and cors_origins_extra.

Machine-readable surfaces

The node does not serve Swagger UI or ReDoc. Use:
  • GET /api/config/schema for the build-time-embedded shape of the config surface, to drive a settings UI.
  • docs/api-surface.md in the agent repository, the generated table of every route and the process that serves it. The route reference below mirrors it.
  • ados logs status --openapi for the OpenAPI document of the Black Box query API.

Route reference

Every route the node serves, generated from the agent’s route table. {name} matches one path segment; {*name} matches the rest of the path. Method WS marks a route that answers only a WebSocket upgrade.

Public routes

These answer without a key.

Native routes

Served by the control front itself.

Residual routes

Forwarded to the residual app on the same port after authentication.

Next steps

Agent CLI reference

The ados commands built on this API.

Systemd services

The units behind each route domain.
Last modified on October 11, 2026