Manager API
The manager serves an HTTP API on its own port, and it is what the paddock CLI talks to. Every verb you can type is a call you can make, so anything the CLI does can be automated directly.
What This API Is For
This is the control plane: starting and stopping model endpoints, downloads, hardware, logs. It is not where you send inference.
Model servers expose the OpenAI and Anthropic APIs on their own ports, and that is where a client belongs. The manager port deliberately does not serve /v1 at all. It does carry relay routes under /api/runners/<port>/v1/..., but those exist so the Studio's browser code never has to hold a runner's key or open a second origin; they are not a proxy for third-party callers, and pointing your application at them buys you a hop and a coupling you do not need.
The line to hold: inference goes to a model server, orchestration comes here.
Base URL and Authentication
The manager listens on port 11500, and that one port answers both schemes. It sniffs the first byte of each connection: a TLS handshake gets TLS, anything else gets plain HTTP. So http://127.0.0.1:11500 keeps working for scripts, the CLI and health checks, while a browser reaching the machine across the network gets HTTPS. A network client that connected with plain HTTP is redirected rather than handed the binary of a TLS response it did not ask for. See Server Configuration for the certificate side.
Authentication follows the same rule as a model server: no key on a loopback bind, and on a non-loopback bind the manager generates one, prints it in the startup banner, and requires it. When auth is on, send Authorization: Bearer <key> on /api paths.
PADDOCK_MANAGER_URL points a client at a manager that is not on the default address; the CLI reads it too.
Browsers get a second route in. POST /auth/login takes the key and returns it as an HttpOnly cookie lasting 30 days, which every subsequent request carries automatically. It sits outside /api so it is reachable before you are authenticated, and the cookie is HttpOnly so the key never lands anywhere a script can read it. That is what lets the Studio work from another machine without every fetch, stream and socket in the app having to attach a header. POST /auth/logout ends it.
# what is running, with live health
curl http://127.0.0.1:11500/api/runners
# start an endpoint and wait for it to come up healthy
curl -X POST http://127.0.0.1:11500/api/servers \
-H "Content-Type: application/json" \
-d '{"model": "qwen3.6-27b"}'Endpoints and Servers
| Endpoint | Description |
|---|---|
GET /api/runners | Running model servers on this machine, its own and adopted, with live health. |
GET /api/servers | Configured endpoints, whether running or not. |
POST /api/servers | Spawn a model server from a spec. Blocks through download, load and the health gate, so the reply means it is actually serving. |
POST /api/servers/preview | The config file a spec would produce, without starting anything. |
POST /api/servers/{port}/start | Start a configured endpoint from its own file. |
GET /api/servers/{port}/file | That endpoint's configuration file. |
POST /api/runners/{port}/switch | Change which model a port serves, keeping the port. |
POST /api/runners/{port}/pin | Mark a runner as never auto-stopped to make room. |
POST /api/runners/{port}/persist | Start-on-boot toggle. Adopted runners refuse, since their configuration is not the manager's to guess. |
GET /api/runners/{port}/logs | That runner's log stream. |
GET /api/elections | The desired-state set: what comes back after a reboot. |
Models and Downloads
| Endpoint | Description |
|---|---|
GET /api/models/catalog | The built-in catalog plus what is already installed. |
GET /api/models/estimate | Will-it-fit math for a model against the detected hardware. |
POST /api/models/pull | Start a download. Returns a job. |
GET /api/models/pulls | Download jobs and their state. |
GET /api/models/pulls/events | Live progress as a stream. |
GET /api/models/pull/{job} | One job's state. |
POST /api/models/pull/{job}/cancel | Cancel a download. |
POST /api/models/pull/{job}/resume | Resume an interrupted one. |
Hardware
| Endpoint | Description |
|---|---|
GET /api/gpu | Current telemetry snapshot: utilization, memory, temperature, power, clocks. |
GET /api/gpus | Every detected device. |
GET /api/gpu/stream | Telemetry as a live stream over WebSocket. |
GET /api/readiness | Whether this machine can serve models at all, and what to do when it cannot. Probed once at startup, since hardware does not change under a running process. |
There is no CUDA-setup endpoint, and there is no longer meant to be one. Paddock ships and fetches no NVIDIA maths libraries: the executables import no NVIDIA DLL, the kernel pack imports only the OS, and the binaries do not contain the names of those libraries at all, so they could not request one. The single NVIDIA binary the engine loads is the display driver itself.
Consuming Telemetry From Another Machine
NVML runs in exactly one process per box, the manager, so the manager is where device-wide telemetry comes from. GET /api/gpu/stream is an ordinary WebSocket and nothing about it is reserved for the Studio: point a dashboard, an exporter or a script at it.
Bind the manager where the consumer can reach it. A non-loopback bind switches authentication on by itself, generating a key and printing it at startup, so exposing the port cannot accidentally expose it unauthenticated:
paddock --host 0.0.0.0Then connect with the key as a Bearer header:
import websockets # pip install websockets
async with websockets.connect(
"ws://gpubox:11500/api/gpu/stream",
additional_headers={"Authorization": "Bearer YOUR_KEY"},
) as ws:
async for frame in ws:
print(frame)Two practical notes. A script sends the key as a Bearer header; there is no query-parameter form. A browser cannot set headers on a WebSocket, so a page authenticates by signing in first through POST /auth/login, which exchanges the key for an HttpOnly session cookie that then rides the handshake. And POST /api/keys mints additional keys, so a telemetry consumer can hold its own revocable one rather than the key that also administers your models.
For pollers, GET /api/gpu returns the same data as a one-shot snapshot. Engine-level counters such as tokens per second and KV cache usage come from each model server's own /api/stats/stream instead, because that is the process that knows them.
Operations
| Endpoint | Description |
|---|---|
GET /healthz | Liveness. No auth. |
GET /api/server | The manager's own version and state. |
GET /api/logs | The merged log stream: manager plus every runner, prefixed by source. |
GET /api/activity | Collected request records, newest first, with optional filters. DELETE purges them. |
The Studio's Own Surface
The rest of what the manager serves belongs to the Studio: conversations, prompts, artifacts, connectors, cloud provider accounts and settings. Those routes are shaped around the UI and change with it, so treat them as internal rather than as an interface to build on. The tables above are the parts that mirror CLI verbs, which is the surface with a reason to stay put.
Every verb on the CLI page is one of these calls. When you are unsure what a call does, run the verb with the manager's log open and watch.