Skip to content

API reference

Andrea de Ruvo edited this page Oct 15, 2026 · 19 revisions

API reference

Every route of this copy of Argus — 149 — grouped as the live Swagger page groups them (Settings → Go to → The API, /api.html on your own server, with Try it out). What each is for, and the ideas behind them, are in The API. Agent marks the routes an agent's key opens (An agent driving Argus); everything is open to the token from the config and to a device's token, except managing devices (config token only). All of it is under /api, behind the token, except /proxy/<port>/ (its own gate).

Files

Reading the filesystem, previewing and searching it. Confined to the configured roots, canonicalised before the check, symlinks out of them refused.

Route What it does Agent
GET /api/download ?path Download a file under its own name
GET /api/favourites Pinned folders, kept on the server
POST /api/favourites Pin or unpin a folder — Pinning is jailed; unpinning is not, and the difference is the whole of this.
GET /api/file ?path&raw?&missing_ok? Read a file, or its tail if it is large — missing_ok=1: a file that is not there answers 204, not 404 — for a page that only wants to know whether something exists (a desk looking for an old pair's PLAN.argus.md), which is not an error and should not show as one in the browser's console.
GET /api/files ?path List a folder
GET /api/fs/usage ?path What a folder weighs, walked on request — What a folder actually weighs, added up by hand.
GET /api/git/diff ?path&what?&ref?&file? The patch: uncommitted work, the last commit, or against a branch — Plain text, as git diff writes it. what: work (not committed, new files included) | last (the last commit) | ref (the work tree against ref, a branch or commit). file narrows it to one file, relative to path. Past 4 MB it is cut and says so in x-truncated. Read only.
GET /api/git/status ?path&quick? What differs from the last commit, under a folder — {repo, folder, under, branch, detached, head: {short, subject, author, when}, files: [{path, state, code, staged, was, add, del}], branches} — repo: null outside a repository. Paths are relative to the folder asked about, and only what is under it is listed. quick=1 answers {repo} alone — and {repo: null}, not a 403 or 404, for a folder Argus does not serve: a terminal asks it about wherever its shell happens to be. Read only.
GET /api/pdf/search ?path&q Find text in a PDF, page by page — Find a string in a PDF and say which pages it is on.
GET /api/search ?path&q Find files by name under a folder
POST /api/ssh/authorize Let a public key in, so SSH asks no password — {key} — one public key line (id_ed25519.pub…), appended to ~/.ssh/authorized_keys of the user Argus runs as, unless it is there already. It gives that key a shell on this machine: the full token only (it already has a terminal here), and the journal records it.
GET /api/ssh/info Where to reach this machine over SSH, for the mount wizard — {user, hosts, host, port} — the user Argus runs as, the names and addresses this machine answers to (the config's address first), and sshd's port. What the commands are written with.
POST /api/ssh/setup One command that mounts a folder on your computer — {path, user, host, port, point, drive, base} → {sh, ps1, until}: one-line commands for macOS/Linux and Windows, behind a link usable once for 15 minutes, that make a key, let it in here, install the tool and mount the folder. Full token only (it can let a key in).
GET /api/stat ?path&missing_ok? Size and modification time, for watching a file — Just enough to notice a file has changed: a window watching a report being regenerated polls this rather than re-downloading the whole thing. missing_ok=1: a file that is not there answers {missing: true} rather than 404 — a window watching a file that was deleted (a team's log after a Reset) asked every few seconds and got an error each time.
GET /api/viewers The viewers and editors mounted for some files — {packs: [{name, title, description, files, entry, edits, binary, source}], broken: [{folder, source, error}], folder} — source is builtin or yours; folder is where yours go (<config>/viewers/<name>/viewer.json). A pack of yours with a built-in's name replaces it.

Writing

Making, moving, deleting, uploading. Every route here is off unless the server was started with --allow-write.

Route What it does Agent
POST /api/drops/keep How long a dropped file is kept — Set drop_keep_days, write it to the config, and sweep straight away.
POST /api/fs/copy Copy
POST /api/fs/delete Delete
POST /api/fs/drop Put a file where drops land, and say where that is — Receive files dropped onto a session.
POST /api/fs/fetch Put a link's file into a folder, without a round trip — Download a URL straight into a directory.
POST /api/fs/mkdir Mkdir
POST /api/fs/move Move
POST /api/fs/rename Rename
POST /api/fs/upload Upload — Receive files into a directory.
POST /api/fs/write Write — Save an edited text file.

Sessions

tmux: what exists, where it is, what it is showing, and how it looks. The terminal itself is a WebSocket.

Route What it does Agent
POST /api/desks Make a desk, or find one by name, and show it — {name, folder?, show?} — what "open a desk called pippo" means to an agent; {rename: "Desk 11", name: "Pippo"} renames one. ✅
POST /api/desks/close Close a desk: its windows go, its sessions stay — {desk} (its name) or {id}: the desk taken out of the preferences and every open page told (desk-closed) — sessions are not touched. Its id is remembered as closed, so a page that has not heard yet cannot save it back. Not the last desk (409). Agents reach it as a request (close_desk), asked of the person unless let through.
POST /api/desks/gone Close the windows whose session has ended — {desk?} — every desk, or the one named: its windows on tmux sessions that no longer exist are taken off, on the machine and on every open page. Only windows: no session is touched (they have already ended), so an agent's key may do it. → {closed: {desk: [names]}}. ✅
DELETE /api/git/worktree ?path&force? Remove a working directory
POST /api/git/worktree Add a working directory on its own branch — A second checkout of the same repository, on its own branch. ✅
GET /api/git/worktrees ?path The working directories of a repository
GET /api/launchers ?versions? What this machine can start — The list a browser is allowed to pick from, and whether each one is really here. ✅
POST /api/relay Hand a sentence to another session, the way a person would — Type text into another session, and press return if asked. ✅
GET /api/resume Agents lost with the tmux server that held them — {lost: [{name, agent, cwd, conversation, how, command, lost_at}]}: sessions that held an agent when their tmux server went away — a reboot, or the server dying. A session that ended while its server stayed up is not here: that one was closed on purpose. how is resume (the same conversation) or fresh (a new one in the same folder, because which conversation it was is not known).
POST /api/resume Bring lost agents back — {names: [...]}. Each comes back under its old name, in its old folder, resuming its conversation where that is known — only names on the lost list, with the command this server recorded, never one sent in the request.
POST /api/resume/forget Stop offering lost agents back
GET /api/runs Orchestrations happening now — What is running, newest first. A page that arrives in the middle of a run draws it from here; after that it is told about every change on the bell stream. ✅
POST /api/runs Post the shape and state of an orchestration — Called by the framework, every time something about a run changes. ✅
GET /api/tmux/buffer The last thing copied inside tmux — The last thing copied inside tmux, so it can reach the device's clipboard.
GET /api/tmux/copymode ?session&missing_ok? Is this session showing history rather than the live end — Is this session showing history rather than the live end? missing_ok=1: a window's own poll, for a session that may have been ended elsewhere — {gone: true}, not a 404.
POST /api/tmux/copymode Back to the live end — Leave history and return to the live end.
GET /api/tmux/copymodes ?sessions? Copy mode of several sessions, in one look — sessions: names one per line. Every terminal on a page asks together, once per round, with one tmux call — only what tmux lists is answered, the rest is gone.
GET /api/tmux/cwd ?session&missing_ok? The directory a session is really in — Where this session actually is, and how well that is known. ✅
GET /api/tmux/cwds ?sessions? Where several sessions are, in one look — sessions: names one per line — a desk's terminals, together, with one tmux call. Only what tmux lists is answered; the rest is {gone: true}.
POST /api/tmux/kill End a session — Killing a session ends everything running in it. The UI asks first.
POST /api/tmux/launch Start an agent, with its first instruction — Create a session, start a launcher in it, wait for it to settle, type the prompt. ✅
POST /api/tmux/new Start a session
POST /api/tmux/note Write what a session is for — {session, note} — a line of your own about what the session is for, shown under its name. Kept in tmux as the session option @argus_note: it goes when the session goes, follows a rename, and a new session with the same name starts without it. Empty removes it.
POST /api/tmux/rename Rename a session
POST /api/tmux/seen Say you have seen an agent waiting, and nothing needs doing — {sessions: [...]}. Each agent that is waiting stops asking for you — no amber on its desk, its window or the counts — until it has worked and stopped again. Kept on the server, so every device agrees. A session that is not waiting is left alone.
GET /api/tmux/sessions Every tmux session on this server ✅
GET /api/tmux/states Which agents are working and which are waiting for you — {states: {session: {agent, state, since}}} for every session with an agent in it.
POST /api/tmux/style Dress one session, without touching the config — Put a look on one session, live.
GET /api/tmux/whoami ?pid Which session a process on this machine runs in — {session} — the tmux session whose pane the process pid descends from, or null. For a program whose environment does not say (Codex's MCP servers get no TMUX_PANE): it sends its own pid and is told where it is. Only processes on this machine, only the sessions of this Argus's tmux server. ✅
GET /api/where/wiring Which agents are set up to say which folder they are in — Which agents here can tell Argus the folder they consider current.
POST /api/where/wiring Set the agents up to say where they are, or undo it — Do the setting up, or take it back out.
GET /api/who What is on this machine, in one answer, for an agent — The one call an agent makes first. ✅

Agents

The Argus plugin in each agent on this machine — installed, which version, installing and updating it with a press, reloading it in the sessions at their prompt.

Route What it does Agent
GET /api/agent/actions What an agent may ask the person to have done — The actions an agent's key cannot take but may request (POST /api/agent/request): each put to the person as Do it / No, unless they let it through (Settings → Agents, or agents_without_asking). danger: deletes files, ends work or lets agents loose. ✅
POST /api/agent/request Ask the person to have something done, and do it on their OK — {action, args, why?, session?, wait?} → {id, state: asked|doing|done|refused|failed| unanswered, text, result?, error?}. The action is checked and put into words first (400 if it cannot be done as asked); the person gets Do it / No; on Do it Argus does it through its own routes with the full key. wait holds up to 300 s for the outcome; come back with GET. ✅
GET /api/agent/request/{ident} ?wait? The outcome of a request ✅
GET /api/plugin The Argus plugin in each agent: installed, which version — {version, agents: [{agent, name, present, installed, outdated}], reload: [sessions], behind: [{session, agent, version, at_prompt}]} — reload is the Claude sessions sitting at their prompt that would take a newer plugin with /reload-plugins (see POST /api/plugin/reload); behind, every session known to run an older plugin than the one offered.
POST /api/plugin Install or update the Argus plugin in one agent — {agent: claude|codex, action: install|update}. Runs the agent's own plugin commands in a login shell, with this checkout as the marketplace when it has none. Changes the agent's configuration, so it is a press in Settings, never something done on its own.
POST /api/plugin/reload Type /reload-plugins into the Claude sessions at their prompt — Only into a Claude whose last word was 'done' — at its prompt, nothing half-typed by us, no permission question open (an Enter there would answer it). The others are left alone and listed; they take the plugin when they next start, or by hand.
POST /api/plugin/seen A session says which plugin version it runs — {session, version} — sent by the plugin's SessionStart hook (argus-check), so a session is known before its first turn.
GET /api/plugin/version The plugin version this copy offers — What argus-check asks at the start of every agent session: cheap, no shell, no agent.

Teams

Agents working on one goal in turns, directed by the server: whose turn it is, the check between turns, and when to stop or ask you.

Route What it does Agent
GET /api/teams The teams of agents, and what each is doing ✅
POST /api/teams Start a team of agents on a goal — {name, goal, template, graph?, path, agents: {node: {launcher, options, worktree}}, check, checks: {node: command}, gate, max_rounds, ws}.
POST /api/teams/check Check a team written as text, Mermaid or YAML — {text} → {ok: true, format, name, goal?, summary, warnings, graph}, or {ok: false, error} naming the line. Always 200: it is the question an agent asks while writing, and "not yet" is an answer, not a failure. Starts nothing. ✅
POST /api/teams/convert A team from Mermaid to YAML, or back, losing nothing — {text, to: yaml|mermaid, base?} → {text}. base is the YAML being edited: what a flowchart cannot say — duties, worktrees, gate, rounds, permissions, reset — is kept from it. {graph, to, base?, name?} instead: a graph from the drawing canvas, written as text, with name, goal, gate, rounds, permissions and reset kept from base. 400 when text is not a team ({error} with preview).
POST /api/teams/done An agent reports its turn — {session, summary, status?, details?} — the MCP tool team_done and argus-say turn. Checked (a judge must say OK, REDO, DONE or BLOCKED; only on its turn; once per turn), then written into the team's log by Argus as the canonical turn, which the director reads. ✅
GET /api/teams/expecting ?session Is this agent stopping before reporting its turn? — For the plugin's Stop guard: {expecting, reason} — true while the agent in this session has a turn it has not reported, at most twice per turn. ✅
POST /api/teams/lint Every problem in a team file, on its line — {text, format?, base?} → {format, ok, problems, where, graph?, name?, goal?, gate?, rounds?, permissions?, summary?}. Each problem: {line, col, end_line, end_col, level: error|warning, message, fix?} — lines 1-based, columns 0-based; a fix is a range and the text to put there. where is each step's line. Always 200: what the editor asks while you type (app/teamlint.py).
POST /api/teams/mermaid Read a team drawn as a Mermaid flowchart — {text, base?, preview?} → {graph}, or 400 naming the line ({error} with preview). base is the team being edited: what a flowchart cannot say (a step's duty, worktree, reads) is kept from it. {graph} the other way → {text}, the flowchart to start editing from (teammermaid.py has the subset understood).
POST /api/teams/pack Check a team pack before taking it in — A team pack (roles and models in one file) read and judged: what can be taken in, and what is refused with the reason. Nothing is stored — the page puts what passed into your own preferences, beside what you made yourself.
GET /api/teams/packs The example team packs this copy ships — examples/team-packs/: offered in the team sheet, taken in only when you press one.
DELETE /api/teams/proposals ?folder Dismiss a team an agent proposed — Off the list; its team.yaml stays in the folder (Team still offers it there).
POST /api/teams/propose Propose a team: written into a folder, for you to start — {text, folder, session?}: the team (Mermaid or YAML) is checked, written as <folder>/team.yaml and a bell rings — tapped, it opens Team on that folder with the team chosen. Starting it is left to the person. 400 when the text is not a team, 409 when the folder holds a team.yaml of the person's own; --allow-write and an existing folder inside the roots. ✅
GET /api/teams/ready ?launchers? Is this machine ready for these agents in a team? — ?launchers=Claude Code,Codex → {launchers: [{name, agent, notes: [{level: warn|info, text, fix?}]}]}: what each would be missing in a team — the plugin, Codex asking before the team tools, Codex's hooks never heard — said before Start (app/readiness.py).
POST /api/teams/ready/fix Fix what a team would be missing — {fix: "codex-team-tools"} lets Codex run team_task and team_done without asking (a copy of its config is kept); {fix: "plugin:claude"|"plugin:codex"} installs or updates the plugin.
POST /api/teams/save Keep a team as the folder's team.yaml — {text, folder, replace?}: the team (Mermaid or YAML) written as <folder>/team.yaml. A team.yaml already there is the base — its duties, worktrees, gate, rounds, permissions and reset are kept — and is replaced only with replace: true; without it the answer is 200 {exists: true, file}, for the page to ask. --allow-write, a folder inside the roots.
GET /api/teams/suggest ?path?&goal? What a goal and a folder suggest for a team — The template a goal reads like, the checks a folder offers (looked for, never run), and place: whether the folder is there, will be made, or is outside what Argus serves — said under the folder box while you type, instead of as a refusal at Start.
GET /api/teams/task ?session An agent's task in its team, structured — What the MCP tool team_task answers: for the agent in this tmux session — its team, goal, role, duty, round, folder, whether it is its turn, what the steps before it said last, the last check, and how to finish. {none: true} when no team has an agent there. ✅
GET /api/teams/vocab What a team file may say, for completion — {top, step, reset, values, roles, conditions, when} — keys with a line each, the values each key takes, the roles and which conditions each kind of step can give. From the schema.
POST /api/teams/yaml Read a team written as YAML — {text} → {name, graph, gate?, rounds?, goal?}, or 400 saying which line is wrong. {graph, name} the other way → {text}, a team file to keep in the project.
DELETE /api/teams/{team_id} Forget a finished team
GET /api/teams/{team_id}/reset What a reset of this team would do — {folder, files, run, log, declared} — the files a reset (or a restart) would delete, resolved from the team's reset: globs, the command it would run and what becomes of the log. What the person confirms, before anything is touched.
POST /api/teams/{team_id}/{action} Continue, pause, stop, reset or restart a team

Notifications

Something has finished, or wants you. Post to it from an agent hook; read the stream to be told as it happens.

Route What it does Agent
POST /api/ask Ask the person a question, and wait for the answer — Put a question in front of whoever is holding the phone, and wait. ✅
GET /api/ask/{ident} ?wait? Wait for an answer, or read one — Come back for an answer that was not there yet. Same question, same id. ✅
POST /api/ask/{ident}/answer Answer one — The tap, or the sentence.
GET /api/asks Questions waiting for an answer — What is waiting on a person, oldest first — the order they should be answered in.
POST /api/bell Ring: something finished, or wants you — Called by an agent hook, or by anything else that knows it has finished. ✅
GET /api/bell/wiring Which agents on this machine are set up to ring — Which agents on this machine are set up to ring, and which are not.
POST /api/bell/wiring Set the agents up to ring, or undo it — Do the setting up, or take it back out.
GET /api/bells ?since? What has rung since a given point — Everything rung after since. ✅
GET /api/bells/stream ?since? Bells as they happen, over one open connection — Bells as they happen, over one connection that stays open. ✅

Prompts

Working out what a path in a session refers to, so it can be opened.

Route What it does Agent
POST /api/fs/locate Locate — Which of these words are files? Read-only, and jailed like everything else.

The machine

CPU, memory, disks, GPUs, and the ports that are listening.

Route What it does Agent
GET /api/labels Your notes on running processes — Every label whose process is still running, by pid.
POST /api/labels Label a process, or clear its label — {"pid": 4242, "label": "cgDist rerun, Listeria batch"} — an empty label clears it. At most 80 characters, one line.
GET /api/network Where this machine is, where you are, and the ssh line between them — Read entirely from this machine and this request — no network call, ever.
POST /api/network/outside Ask a stranger what this machine's public address is — The only thing in Argus that tells somebody else anything, and it is a POST for that reason: it is an action with a consequence, not a reading.
GET /api/overview What is happening on this machine, in one cheap call — Everything a board watching several machines needs, and nothing it does not. ✅
GET /api/runnable What this machine offers to start and stop — The names, and whether each is up right now. Not the commands.
POST /api/runnable/{name}/{action} Start or stop one of them — action is start or stop, and name must be something this machine published.
POST /api/shutdown Stop this Argus — Stops the server. Nothing here can start it again.
GET /api/system ?brief? CPU, memory, swap, GPUs, disks, uptime

Ports

Standing in front of a service that only listens on loopback, one port at a time.

Route What it does Agent
GET /api/ports What is listening, and what is holding it
POST /api/ports Open or close a port for proxying — Opening a port also hands back the cookie the proxied page needs: its own stylesheets and scripts cannot carry an Authorization header.
DELETE /proxy/{port}/{path} Reach a service that only listens on loopback
GET /proxy/{port}/{path} Reach a service that only listens on loopback
OPTIONS /proxy/{port}/{path} Reach a service that only listens on loopback
PATCH /proxy/{port}/{path} Reach a service that only listens on loopback
POST /proxy/{port}/{path} Reach a service that only listens on loopback
PUT /proxy/{port}/{path} Reach a service that only listens on loopback

Setup

What this server allows, which languages it has, and the tmux configuration it reads.

Route What it does Agent
GET /api/config What this server allows
GET /api/devices The devices that may get in — Names, when each was added and when each was last used. Never the tokens: only a hash of those is kept, and the plain form existed once, when it was created.
POST /api/devices Give a device its own token — Mints one and returns it once. There is no way to see it again — the file holds only a hash — which is the same bargain GitHub makes for a personal access token, and for the same reason.
DELETE /api/devices/{device_id} Take a device's token back — Takes effect on the next request that device makes: the file is re-read every time a token is presented, precisely so that revoking is not something you have to restart for.
POST /api/devices/{device_id} Rename a device — The token is untouched: this is the label, not the key. Renaming does not sign anything out, which is the whole reason it is a separate action from revoking.
DELETE /api/journal ?older_than? Empty the journal, or the old part of it — Everything, or everything before a cutoff given in seconds.
GET /api/journal ?limit? What has been done here, and what was refused — Most recent first. Only the token from the config may read it: a record that a possibly-stolen device can read is a record that tells whoever took it what you can see.
POST /api/language Add or replace a language catalogue — Anyone can translate the catalogue and add it here; it is a flat JSON object keyed by the English strings, so it needs no tooling to produce.
GET /api/language/{code} One language catalogue
GET /api/languages The interface languages available
GET /api/prefs Everything the browser remembers, from here — The desks, the windows and where they sit, the prompt library, the placeholder sets, the shortcuts, the theme — the sixty keys that used to live only in one browser's storage. ✅
PATCH /api/prefs Change some of them, leaving the rest alone — Merge the keys you send into whatever is there now, and nothing else.
PUT /api/prefs Replace the whole document — The whole thing at once — an import, a restore, a browser adopting a machine that has nothing yet.
GET /api/since ?at&folder? What happened while you were not looking — Everything that changed since a moment, in one answer.
GET /api/themes The themes: Argus's interface and terminals, as one file each — {themes: [{id, name, type, colors, terminal, author, source}], broken: [{file, source, error}], folder} — source is builtin or yours; yours are <config>/themes/<id>.json, and one with a built-in's id replaces it.
POST /api/themes Save a theme of yours — A theme as /api/themes lists them (id optional: made from the name). Every colour is checked to be a colour — #rgb, #rrggbb, #rrggbbaa, rgb() — and nothing else is kept.
POST /api/themes/import Read a theme from iTerm2, Windows Terminal or VS Code — {text, filename} → the theme, not saved: what the editor opens to look at and save. An Argus theme, an .itermcolors, a Windows Terminal scheme (or its whole settings.json) or a VS Code colour theme.
DELETE /api/themes/{theme_id} Delete a theme of yours — Only one of yours: a built-in has no file here to delete (yours replacing it goes, and the built-in is back).
POST /api/tmux/source Hand the tmux config to every session, after trying it safely — Make every session pick up the configuration file.
GET /api/todo The list of things to do — Kept on the server rather than in the browser, so the note you wrote at the desk is on the phone as well. That is the whole reason it is here and not in the preferences. ✅
POST /api/todo Add something to do ✅
DELETE /api/todo/{ident} Take one off the list
PATCH /api/todo/{ident} Change one: its words or its state (by id or #number) ✅
POST /api/tools/link Replace old copies of Argus's commands with links — The copies /api/version calls old (old_tools), each replaced by a link into this checkout — so the next pull updates them too. The old file is kept beside it (<name>.argus-old-<time>). Only those exact paths; --allow-write.
GET /api/version What is running here, and whether anything newer exists — Answers immediately from a day-old cache; the question is only asked of GitHub when that cache is stale. Switched off, it answers about this machine and stops there, which is also what it does when there is no way out to the network.

Sync

Route What it does Agent
DELETE /api/sync/dir ?p Remove an empty folder
POST /api/sync/dir ?p Make a folder
DELETE /api/sync/file ?p&base? Remove one file (into the trash)
GET /api/sync/file ?p One file of the synced folder
PUT /api/sync/file ?p&m&base? Write one file of the synced folder — The body is the file. m: its modification time (ns) on the client, given to the copy here.
GET /api/sync/hello Who this sync key is
GET /api/sync/keys The sync keys
POST /api/sync/keys Make a sync key for one folder
DELETE /api/sync/keys/{key_id} Take a sync key back
GET /api/sync/manifest ?since? Every file in the synced folder, with size and time

Clone this wiki locally