REST API¶
Genswarms exposes a pure JSON REST API served by Phoenix (no HTML/frontend is included). The same server also hosts a WebSocket endpoint for real-time streaming — see websocket.md.
All routes are defined in lib/genswarms_web/router.ex and implemented by the controllers in lib/genswarms_web/controllers/. This page documents every route, derived directly from those sources.
Base URL and conventions¶
- Base URL:
http://localhost:4000(the port is set by thePORTenv var, default4000). - The API pipeline accepts
application/jsononly. Send request bodies as JSON and setContent-Type: application/json. - CORS is enabled for all origins (
origins: "*", all headers and methods allowed) via Corsica, so browser-based frontends can call the API directly. - Successful responses return a JSON object. Errors return a JSON object with an
error(string) orerrors/validfield and an appropriate HTTP status (400,404,500). - Most endpoints work for both in-process swarms and daemon swarms; the controller falls back to the SQLite registry / Docker when a swarm runs in a separate OS process.
API info¶
| Method | Path | Description |
|---|---|---|
| GET | / | API metadata: name, version, endpoint index, and a WebSocket/route summary |
The root returns a static descriptor including name, version ("1.0.0"), description, an endpoints map, a websocket section, and a documentation map summarizing the main route groups. The websocket section advertises the channel URL (/swarm), the channel topic pattern (swarm:{swarm_name}), and the client→server / server→client event lists (see websocket.md for details).
Swarm management¶
| Method | Path | Description |
|---|---|---|
| GET | /api/swarms | List all swarms |
| POST | /api/swarms | Create a swarm from an inline config or a config path |
| GET | /api/swarms/:name | Get detailed swarm status |
| DELETE | /api/swarms/:name | Stop a swarm (?purge=true to delete all data) |
| POST | /api/swarms/:name/pause | Pause (freeze) all of the swarm's containers |
| POST | /api/swarms/:name/resume | Resume a paused swarm |
| POST | /api/swarms/:name/restart | Restart the swarm (?delete=true for a clean slate) |
| POST | /api/swarms/:name/message | Route a message between two agents |
| POST | /api/swarms/clean | Remove stopped/crashed swarms (?all=true also clears all events) |
Notes:
GET /api/swarmsreturns{"swarms": [ ... ]}.POST /api/swarmsaccepts either{"config": { ... }}(an inline swarm config object) or{"config_path": "path/to/config.exs"}. On success it returns201 Createdwith{"status": "created", "swarm_name": "..."}. On a config/start error it returns400with{"error": "..."}. Missing both fields returns400with{"error": "Missing 'config' or 'config_path' parameter"}.GET /api/swarms/:nameenriches the status withtopology, per-agentbackend_type,skills_paths,container_name,container_status, per-objecthandler_module/source_file, and afile_pathsmap (config,data_dir=~/.subzeroclaw/swarms/<name>,log=.genswarms/logs/<name>.log). Returns404with{"error": "Swarm not found"}if the swarm is unknown.DELETE /api/swarms/:namereturns{"status": "stopped"|"purged", "swarm_name": "...", "config_path": ...}. With?purge=trueit also deletes swarm files and registry rows. Returns404for an unknown swarm.POST .../pauseand.../resumereturn a count:{"status": "paused", "swarm_name": "...", "containers_paused": N}/{"status": "resumed", "swarm_name": "...", "containers_resumed": N}. A404is returned for an unknown swarm; a backend failure returns500.POST .../restartreads the saved config path from the registry;?delete=truedeletes data before restarting. Returns{"status": "restarted", "swarm_name": "...", "delete_data": bool}. Returns404if the swarm (or its config path) is unknown.POST /api/swarms/:name/messagerequires{"from": "...", "to": "...", "content": "..."}and returns{"status": "routed", "from", "to", "swarm"}. Missing fields return400with{"error": "Missing 'from', 'to', or 'content' parameter"}.POST /api/swarms/cleanreturns{"status": "cleaned", "swarms_removed": N, "events_cleared": bool}.
Agent operations¶
| Method | Path | Description |
|---|---|---|
| GET | /api/swarms/:name/agents | List agents and their status |
| GET | /api/swarms/:name/agents/:agent | Get a single agent's status |
| POST | /api/swarms/:name/agents/:agent/task | Send a task to an agent |
| POST | /api/swarms/:name/agents/:agent/restart | Restart an agent |
| GET | /api/swarms/:name/agents/:agent/history | Get the agent's message history (?limit=, default 100) |
| GET | /api/swarms/:name/agents/:agent/logs | Get the agent's conversation logs |
| GET | /api/swarms/:name/agents/:agent/skills | Get the agent's skill contents |
| PUT | /api/swarms/:name/agents/:agent/skills/:skill | Update one of the agent's skill files |
Notes:
POST .../taskrequires{"task": "..."}and returns{"status": "sent", "agent", "task"}. For daemon swarms the task is queued in SQLite for the daemon to pick up. Missingtaskreturns400with{"error": "Missing 'task' parameter"}.GET .../agentsreturns{"agents": [ ... ]};GET .../agents/:agentreturns the status map directly, or404with{"error": "Agent not found"}.POST .../agents/:agent/restartreturns{"status": "restarted", "agent": "..."},404if the swarm is unknown, or500on failure.GET .../historyandGET .../logsreturn{"history": [...]}/{"logs": [...]}.GET .../skillsreturns{"skills": ...}. A missing agent returns404.PUT .../skills/:skillrequires{"content": "..."}and returns{"status": "updated", "skill": "..."}. A failure returns500.
Adding and removing agents at runtime uses
POST /api/swarms/:name/agentsandDELETE /api/swarms/:name/agents/:agent— see Dynamic topology and scaling below.
Messages¶
| Method | Path | Description |
|---|---|---|
| GET | /api/swarms/:name/messages | Get the swarm's recent inter-agent message log |
Notes:
GET /api/swarms/:name/messagesreturns{"messages": [ ... ]}from the router's message log. Accepts?limit=(default 100). This is the routed-message history (who sent what to whom); for structured observability events use the Events endpoints instead.
Dynamic topology and scaling¶
These endpoints mutate a running swarm and persist the change as an overlay (see Overlay and snapshot).
| Method | Path | Description |
|---|---|---|
| GET | /api/swarms/:name/topology | Get the swarm's topology (adjacency list) |
| PATCH | /api/swarms/:name/topology | Add and/or remove topology edges |
| POST | /api/swarms/:name/agents | Add a new agent to a running swarm |
| DELETE | /api/swarms/:name/agents/:agent | Remove an agent from a running swarm |
| POST | /api/swarms/:name/agents/:base/scale | Scale an agent group to a target count |
Notes:
GET .../topologyreturns{"topology": [{"from": ..., "targets": [...]}, ...]}or404for an unknown swarm.PATCH .../topologyaccepts{"add": [...], "remove": [...]}. Each edge may be["from", "to"]or{"from": "...", "to": "..."}; both endpoints of an edge must be strings, and any edge that doesn't parse is silently ignored. Returns{"status": "ok", "added": N, "removed": M}(the counts reflect the parsed edges actually applied). A mutation error returns400.POST .../agentsaccepts an agent spec (name,backend,skills,model,endpoint,presets,config) plus optionalconnections(outgoing targets) andincoming(sources).backendmay be a string (e.g."local","mock") or an object:{"type": "docker", "image": "coder"},{"type": "ssh", "host": "user@host"},{"type": "bwrap", "opts": { ... }}, or{"type": "mock"}. Returns201 Createdwith{"status": "added", "name": "..."}, or400with{"error": "..."}on failure.DELETE .../agents/:agentreturns{"status": "removed", "name": "..."}or404with{"error": "..."}.POST .../agents/:base/scalerequires an integer{"count": N}(count >= 0) and returns{"status": "ok", "result": {"added": [...], "removed": [...], "failed": [{"name", "reason"}]}}(theadded/removed/failed[].namevalues are strings). A missing/non-integer/negativecountreturns400with{"error": "Missing or invalid 'count'"}; a scaling error returns400with{"error": "..."}.
Objects¶
Objects are the non-agentic components of a swarm. See objects.md.
| Method | Path | Description |
|---|---|---|
| GET | /api/swarms/:name/objects | List objects with their lifecycle state |
| POST | /api/swarms/:name/objects | Add an object to a running swarm |
| GET | /api/swarms/:name/objects/:object | Get an object's live read-only state |
| DELETE | /api/swarms/:name/objects/:object | Remove an object from a running swarm |
Notes:
GET .../objectsreturns{"objects": [{"name", "state", "handler"}, ...]}, wherenameis a string andhandleris the inspected handler module (ornull).POST .../objectsaccepts an object spec (name,handlermodule name,backend,config) plus optionalconnections/incoming. Returns201 Createdwith{"status": "added", "name": "..."}, or400with{"error": "..."}on failure.GET .../objects/:objectreturns{"object": "...", "state": <handler domain state>}. The framework imposes no schema on the state. An unknown object returns404with{"error": "Object not found"}.DELETE .../objects/:objectreturns{"status": "removed", "name": "..."}or404.
Overlay and snapshot¶
A swarm's effective config is its seed config combined with an overlay of runtime mutations (added/removed agents, topology edits, scaling).
| Method | Path | Description |
|---|---|---|
| GET | /api/swarms/:name/overlay | Show the recorded overlay events |
| DELETE | /api/swarms/:name/overlay | Clear the overlay |
| POST | /api/swarms/:name/snapshot | Return the effective config (seed ⊕ overlay) as Elixir source |
Notes:
GET .../overlayreturns{"swarm": "...", "events": [{"op": ..., "payload": ...}, ...]}.DELETE .../overlayreturns{"status": "cleared", "swarm": "..."}.POST .../snapshotresponds withContent-Type: text/x-elixirand200, with the effective config rendered as a.exssource body (not JSON; produced byGenswarms.Config.ExsWriter). Returns404with a JSON{"error": "..."}if the swarm is unknown.
Events¶
Event queries read from the durable, cross-process EventStore (SQLite by default), so events from daemon swarms in other BEAM nodes are visible. See observability.md.
| Method | Path | Description |
|---|---|---|
| GET | /api/events | Query events with filters |
| GET | /api/swarms/:name/events | Query events for one swarm |
| GET | /api/swarms/:name/agents/:agent/events | Query events for one agent |
Shared query params (all optional):
| Param | Description |
|---|---|
| level | error, warning, info, or debug |
| category | backend, routing, agent, swarm, or system (see note) |
| swarm | Swarm name (implied on the scoped routes) |
| agent | Agent name (implied on the agent route) |
| event_type | A specific event type |
| minutes | Only events from the last N minutes |
| limit | Max events to return (default 100) |
Each response includes events (a list) and count. GET /api/events also echoes the normalized filters as a query map; the scoped routes echo swarm (and agent on the agent route). Every event is {id, timestamp, level, category, swarm, agent, event_type, message, metadata}; timestamps are ISO-8601 strings.
Note:
categoryis resolved withString.to_existing_atom/1, so only category names that already exist as atoms in the running system are accepted. The controller's documented set isbackend, routing, agent, swarm, system, but the broader observability taxonomy and the CLI also emit anobjectcategory — passingcategory=objectworks as long as that atom has been created (e.g. after any object event has been logged). See observability.md for the full taxonomy.
Skills¶
See skills.md.
| Method | Path | Description |
|---|---|---|
| GET | /api/skills | List available skill files (?path= overrides the search root) |
| GET | /api/skills/:name | Get a skill's content by name |
Notes:
GET /api/skillssearches the configured skills directory (the:genswarms/:skills_dirapp env, defaultpriv/skills, expanded to an absolute path) recursively and returns{"skills": [{"name", "path", "relative_path", "category"}, ...], "base_path", "count"}.categoryis the relative subdirectory (or"default"for files at the root). Pass?path=to search a different root.GET /api/skills/:namereturns{"name", "path", "content", "size"}(sizeis the byte length), or404with{"error": "Skill not found"}if the skill is not found. The.mdextension is optional in the name, and nested skills are matched by a recursive search.
Config validation¶
See configuration.md.
| Method | Path | Description |
|---|---|---|
| POST | /api/config/validate | Validate a swarm config |
POST /api/config/validate accepts one of:
{"config": { ... }}— an inline config object (must be a JSON object).{"config_path": "path/to/config.exs"}— a.exs,.json, or.yamlfile path.{"content": "...", "format": "exs"|"json"|"yaml"}— a raw config string ("yml"is accepted as an alias foryaml; an unrecognized format falls back toexs).
On success it returns {"valid": true, "config": <summary>} (the config_path / format form also echoes that input field) where the summary includes name, agent_count, object_count, topology_edges, and per-agent (name, backend, skills, model), per-object (name, handler), and topology (from/to) details. On a validation failure it returns 400 with {"valid": false, "errors": [...]}. A non-existent config_path returns 404 with {"valid": false, "errors": ["File not found: ..."]}. Sending none of the accepted fields returns 400 with {"error": "...", "usage": { ... }}.
Examples¶
List all swarms:
Create a swarm from a config file on the server:
curl -X POST http://localhost:4000/api/swarms \
-H "Content-Type: application/json" \
-d '{"config_path": "examples/research.exs"}'
Send a task to an agent:
curl -X POST http://localhost:4000/api/swarms/my-swarm/agents/researcher/task \
-H "Content-Type: application/json" \
-d '{"task": "Summarize the latest results."}'
Add an agent to a running swarm and wire it into the topology:
curl -X POST http://localhost:4000/api/swarms/my-swarm/agents \
-H "Content-Type: application/json" \
-d '{"name": "reviewer", "backend": {"type": "docker", "image": "code"}, "incoming": ["coder"]}'
Snapshot the effective (seed ⊕ overlay) config as runnable Elixir:
See also¶
- cli.md — the
swarmCLI, which wraps many of these endpoints. - websocket.md — real-time streaming over the WebSocket channel.
- programmatic.md — driving swarms directly from Elixir.
- configuration.md — the swarm config DSL used by create/validate.
- observability.md — the event categories and levels surfaced by the Events endpoints.