Skip to content
Control plane API Overview of the /v1 REST surface, the agent WebSocket protocol, auth, and the error shape.

Control plane API

The MZPanel control plane exposes a REST API (used by the dashboard) and a WebSocket protocol (used by the on-VPS agent). This page is an overview, not an exhaustive endpoint list.

There are two callers, each with its own credential (the same model as Security model):

CallerCredential
Dashboard → APIA session cookie (httpOnly, SameSite=Strict, Secure), set at sign-in.
Agent → API (WS)A long-lived agent token (Authorization: Bearer …), obtained at registration from a one-time install token.

REST endpoints live under /v1 at api.mzpanel.com and are grouped by resource — for example servers, sites, jobs, backups, team, and account. Operations that change a VPS are dispatched as jobs: the API queues the job and relays it to the target agent over the WebSocket, then streams the result back. Inputs are validated with Zod schemas on every route.

The agent dials outbound to wss://ws.mzpanel.com/v1/agent and holds one persistent connection — the VPS opens no inbound ports. Messages are JSON envelopes with a shared shape:

type Envelope = {
v: 1; // protocol version
id: string; // message UUID
ts: number; // unix ms
type: string; // discriminator (hello, heartbeat, log, job_done, cmd, …)
ref?: string; // optional reference to another message id
payload: unknown;
};

The flow, briefly:

  1. Agent connects and sends hello; the API replies welcome with the license envelope (signed).
  2. Agent sends a heartbeat every 30s with a metrics snapshot.
  3. The API sends cmd envelopes; the agent runs them natively and streams log lines, ending with job_done (exit code + duration).
  4. The agent pushes event envelopes for things that happen out of band (backup done, SSL renewed, service down).

See Architecture for the connection model and docs/03 for the full message catalog.

API errors use a consistent envelope — never a raw stack trace:

{ "error": { "code": "string", "message": "human readable", "details": {} } }

details is optional and present only when extra context helps (e.g. validation failures). Clients should switch on error.code, not on message.