The Agent API is a way to control MultiAgent without the interface. Other programs and agents get access to servers, agents, tools and products through a set of commands locked behind a key. Whatever you do with buttons in the browser, an API caller can do automatically: for example, your assistant can create a server, deploy a new agent and check that it works. The command set mirrors what the interface can do, but it is safer: secrets are accepted for writing only, there are no arbitrary commands, and long operations follow the proven “start it, then poll it” pattern. A program learns the available commands from the API itself — no separate documentation needed.
The key and how access is checked
Every request to /api/agent/ must carry the key in the X-API-Key header. The key itself is not stored on the server: the environment variable MULTIAGENT_API_KEY holds its “fingerprint” (a SHA-256 hash — a short string the key cannot be recovered from). On every request the server computes the fingerprint of the presented key and compares it with the stored one.
- Variable not set — the whole API answers
503, “Agent API is disabled”: the API is off entirely; - Wrong or missing key —
401 Unauthorized(the attempt is logged with the client’s address); - Separate surfaces — the interface key does not open the API, and the API key does not open the browser pages.
Watch the end of the address: /api/agent/... is the program interface, opened by the key; /api/agents/... is the regular interface with sign-in by user account.
The catalog: GET /api/agent
The first request worth making is to GET /api/agent: it returns the full command catalog. It describes what MultiAgent controls, how the key check works (what the 503 and 401 errors mean), the basic concepts (server, agent, product), how long operations run and how secrets are passed — plus every command: method, address, request fields and the response shape. The catalog is generated from the code, so it is always current — anyone who knows just the address and the key needs no separate documentation.
Long operations: start it, then poll it
Long operations — installing Docker, deploying agents and tools — all work the same way. The start command immediately returns an operation number, and your program asks for the status from time to time until it sees “done” or “failed”. It is like an order in a restaurant: you don’t have to wait by the stove — you get an order number and come back when the dish is ready. Re-issuing the start command is unnecessary — the operation is already running.
POST /api/agent/servers # created a server
→ 202 { "operationId": "op_123", "message": "..." }
GET /api/agent/operations/op_123 # poll the status
→ { "operationId": "op_123", "kind": "...", "status": "running",
"lines": [{ "text": "...", "isError": false }], ... }
GET /api/agent/operations/op_123 # final state
→ { "operationId": "op_123", "status": "done", "lines": [...], ... }
The status response contains lines — the operation’s progress lines in order, with the last line naming the outcome. Operation records live in memory and not forever: an unknown or expired operation number answers 404 with a hint to check the resource’s current state. Operations started from the browser are checked the same way.
Secrets — write-only
No API response ever contains a secret’s value — passwords, private keys, tokens. They are accepted on create and update, stored encrypted, and only indirect signs go out: whether a password is set (the hasPassword flag), whether there is a private key (hasPrivateKey), the names of extra variables, masked values.
There is deliberately no way to “peek” at a secret through the API: no revealing a service password, no exposing agent secrets, extra variable values, changing the manager’s password, or generating SSH keys. Handing out a generated private key via the API would give anyone holding the API key direct access to the server — such actions are available only to a person in the interface.
Only predefined operations
Through the API you cannot run an arbitrary command on the server — only MultiAgent’s predefined actions: install, clean, start, stop, deploy. Every operation’s input is limited to identifiers, names, ports and validated fields; no command accepts scripts or arbitrary configuration. Even the destructive actions (deleting a server or a container with its data, cleaning up Docker) are predefined, well-bounded operations. This protection means: even a mistake or a failure in an automated client cannot do anything unplanned.
What you can do through the API
- Servers — the list with connection state, viewing one server’s data and usage, checking installed services (docker, postgres, forgejo, rsync, nginx, the home page, the central login), an SSH connection test, a check with a list of findings and fix actions, create, update, delete — plus the long repair operations: install Docker, check, create folders, clean up Docker, start a stopped Postgres, install postgres/forgejo/backups, import agents found on the server;
- The server’s public access — setting up HTTPS, the central login and the home page through the API, without the interface: Nginx status and install, certificates (custom, self-signed, a Let’s Encrypt request — a long operation with progress), enabling the HTTPS port, custom routes (create, update, delete, protect behind the central login), the central login lifecycle — install, apply, remove while keeping data and secrets — and the home page: installing and updating the static board at the server’s root;
- Server handover and one-time sign-in — accepting server records from another MultiAgent instance (this is how the handover works) and issuing a one-time code to sign into the interface without a password — used by the “Manage” button on the server page;
- Server checks — running a check returns structured results for every checklist row (code, status: passed/warning/failed/skipped, details and a fix action); the action runs the same one-step fix as the “Check and fix” button in the interface, so the whole “check — fix — re-check” loop works over the API; checks whose prerequisites are missing are marked skipped and don’t count;
- Agents — the list with type (regular agent or product), slug, port, public address and container state; viewing state (status, logs, statistics, configuration, API check); creating — the port is picked automatically, for products — from the product catalog with variables; the lifecycle: deploy, start, stop, restart, recreate, delete. There is no separate “pull image” command — recreating pulls a fresh image itself;
- The Git plumbing and agent databases — the server’s Forgejo repository list (honoring the organization from the “Company” field), creating a repository and linking it to an agent (or linking an existing one), checking external database credentials before creating an agent, testing an agent database’s own credentials, and reading references — what currently points at the agent (handy before a deletion);
- Models — MultiAgent’s model catalog (list, create, update, delete), pushing a model onto chosen agents — the same-named agent record is replaced, with the outcome per agent; changing a private variable recreates the agent’s container — and importing from an agent (same-named catalog records are replaced, keeping the schedule and icon); private variables are masked in responses — only the “value is set” sign goes out;
- Skills — MultiAgent’s skill catalog (list, read files, create, update, delete — the copies on agents remain), pushing a skill onto chosen agents as an exact replacement of the same-named folder, and importing from an agent with replacement on name match;
- Licenses — MultiAgent’s own license status and setting a new key; the agent license pool: list, create, update, delete — the key is accepted for writing only and always masked, and a license in use cannot be deleted (a conflict with the reference count);
- Tools and products — tool templates (the catalog and the full description with variables) and installing them on a server with variable validation; the product catalog is readable — with descriptions of its variables.
Example calls
# The catalog — the surface's self-describing documentation
curl -H "X-API-Key: my-key" https://multiagent.example.com/api/agent
# The server list with connection state
curl -H "X-API-Key: my-key" https://multiagent.example.com/api/agent/servers
# Create an agent and deploy it (202 + polling operations/{id} for status)
curl -X POST -H "X-API-Key: my-key" -H "Content-Type: application/json" \
-d '{"name":"bot","serverId":3,"slug":"bot"}' \
https://multiagent.example.com/api/agent/agents
curl -X POST https://multiagent.example.com/api/agent/agents/42/provision
# Server checks: 202, the operation carries structured results with fix actions
curl -X POST -H "X-API-Key: my-key" https://multiagent.example.com/api/agent/servers/3/check
# polling operations/{id} returns checks[]: code, status, details and a fix action
# Model: push to chosen agents (replaces the same-named record)
curl -X POST -H "X-API-Key: my-key" -H "Content-Type: application/json" \
-d '{"agentIds":[3,4]}' \
https://multiagent.example.com/api/agent/models/12/push
The error format is uniform — the response body is always { error, message }. Bad input — 400 InvalidRequest; an unknown resource — 404 NotFound; conflicts (for example, deleting a server that agents still reference) — 409 Conflict with the number of affected agents; license limits on creating — 403 with an expired-license flag; an unreachable server on a check that needs it — 502 RemoteError with the reason. API messages are in English.