Hive Agent API
The Agent API lets AI agents, scripts and other tools talk to Hive: list projects and their session status, read and write shared notes, create handovers, send notifications and (optionally) drive sessions.
There are two ways to use it:
- The built-in
hiveMCP server — every session started from Hive gets it automatically. Agents just call the tools. No setup needed. - The HTTP API — for scripts and anything else. Described below.
The API only listens on
127.0.0.1and every request (except/v1/health) needs a bearer token. Requests that carry a browserOriginheader are rejected, so web pages cannot call it.
Connecting
| Base URL | http:// (port configurable in Settings → Agent API) |
| Auth | Authorization: Bearer <token> |
| Token | Settings → Agent API → Access token, or %APPDATA%\Hive\agent-api.json; for an app that only shows Hive, a viewer token (below) |
| Finding Hive | %APPDATA%\Hive\agent-api-discovery.json, while the API listens (below) |
| Format | JSON request and response bodies (Content-Type: application/) |
Sessions started from Hive receive these environment variables, so an agent can call the API with no configuration. An agent's token works like the workspace token, except on the task board, where it confines the agent to its own project's cards (see Task board):
| Variable | Value |
|---|---|
HIVE_API_URL |
Base URL of the API |
HIVE_API_TOKEN |
The agent's own bearer token for this launch (not the workspace token in agent-api.json): the API knows from it which project's agent is calling, and it stops working when the agent stops |
HIVE_API_TOKEN_FILE |
Path of the file holding that token, which Hive rewrites at each launch |
HIVE_PROJECT |
Name of the project the session belongs to |
HIVE_AGENT |
Name of the agent running the session (e.g. Agent 1) |
HIVE_PROJECT_PATH |
Full path of the project |
HIVE_WORKSPACE |
Full path of the workspace |
HIVE_PROVIDER |
The provider running the session: claude-code, codex or copilot |
HIVE_SESSION_ID |
The session ID, when it is known at launch (Claude Code; Codex chooses its own ID with the first prompt, so its sessions don't get this) |
HIVE_RUN_ID |
An ID for this launch of the agent, unique even before the session ID is known |
Viewer tokens and the discovery file
An app that shows Hive, such as Apiary, reads it with a viewer token: read-only, for the whole workspace. The user makes one in Settings → Agent API → Viewer tokens, named for its app, and pastes it into the app once; revoking it there refuses its next request and ends its open event stream. Viewer tokens are kept in %APPDATA%\Hive\agent-api-viewers.json, never in a project.
- A viewer sees what the workspace token sees: every
GETroute and the event stream (GET /v1/events), every project.GET /v1/statusgives itscalleras{ "role": "viewer", "name": "Apiary", "scope": "workspace", "readOnly": true }. - Every other request is refused (403), before its body is read or its route looked up: every
POST,PUT,PATCHandDELETE, including the waits (POST /v1/tasks/wait,POST /v1/agents/wait) and progress reports. It changes nothing. - It works while the Agent API is on, as the workspace token does.
The discovery file, agent-api-discovery.json in Hive's profile (%APPDATA%\Hive; the development build's is %APPDATA%\Hive-Dev), tells another app where a running Hive's API is, without reading Hive's private config.json:
json{ "app": "Hive", "version": "0.6.1", "api": 4, "url": "http://127.0.0.1:47821", "port": 47821, "pid": 12345, "startedAt": "2026-10-11T19:30:00.000Z", "note": "…" }
It holds no token. Hive writes it when the Agent API starts listening (at start, or when it is turned on or its port changes) and removes it when the API is turned off and when Hive quits. A Hive that ended without quitting (a crash) leaves it behind, naming a process that is gone: check with GET /v1/health before using it.
Example (PowerShell)
powershell$h = @{ Authorization = "Bearer $env:HIVE_API_TOKEN" }
Invoke-RestMethod "$env:HIVE_API_URL/v1/projects" -Headers $h
Example (bash / curl)
bashcurl -s -H "Authorization: Bearer $HIVE_API_TOKEN" "$HIVE_API_URL/v1/projects"
Errors
Errors return a non-2xx status and { "error": "message" }. The message is in English, whatever language Hive shows: the window shows the same refusal in the user's language, and replies never carry that translation.
| Status | Meaning |
|---|---|
| 400 | Invalid request (missing field, bad JSON, invalid session ID, agent needed because the project has several) |
| 401 | Missing or wrong token |
| 403 | Blocked (cross-origin request, a disabled feature such as session input, a change only the user makes, such as archiving a card or putting Done in order, or any request but a GET with a viewer token) |
| 404 | Unknown route, project, card, skill or file, or one outside the caller's scope (another project's card looks missing to its agents) |
| 409 | Conflict with the current state (no workspace open, the project has no agents, agent already running or starting or busy, conversation open in another agent, session archived, provider turned off or not installed…) |
| 500 | Unexpected error; details are in Hive's log |
| 413 | Body larger than 2 MB, or a skill file larger than 512 KB |
Several windows
Hive can show several workspaces, each in its own window (File → New Window). The API serves all of them:
- A request is for one workspace: the one named by the
X-Hive-Workspaceheader (its folder path, URL-encoded) or theworkspacequery parameter (its name or path), else the only one open. A name that two open workspaces share (C:\Clients\fooandD:\Clients\foo) is refused with 409, listing both paths: name the workspace by its path instead. A path finds the workspace by where its folder is, so another name for it (a junction, an 8.3 short name) works too. ThehiveMCP server sends its session's workspace, so an agent's tools always see its own workspace. {name}in/v1/projects/{name}is a project in the request's workspace. With several windows open and no workspace named, a name is looked up in every open workspace: a name found in two answers 409; say<workspace>/<project>instead (/encoded as%2Fin the path:/v1/projects/work%2Fapi).- Calls about a whole workspace (
/v1/workspace, shared notes, the task board, skills without a project, MCP servers) answer 400 when several are open and none is named.
Endpoints
Health
GET /v1/health — no token required.
json{ "ok": true, "app": "Hive", "version": "0.1.0" }
Status
GET /v1/status — what is running and who you are to it: the app's version, the API's contract version (api.version, which goes up when a route or field changes in a way a client would notice), the caller (role: agent with its project and agent name, confined to that project; assistant with its workspace and control level; viewer, a read-only viewer token, with its name; or api, the workspace token), the guidance Hive gives sessions (guidance.revision, and each of the request's workspace's Hive skills with who it is for, its content hash, and for a bundled one whether this copy is Hive's or edited and whether a newer version is there to take), each provider's install info, the request's workspace (null if several are open and none is named), every open workspace (workspaces) and running sessions. Start here before calling anything else.
json{
"app": { "name": "Hive", "version": "0.3.1" },
"api": { "version": 5 },
"caller": { "role": "agent", "project": "web", "agent": "Agent 1", "scope": "project" },
"guidance": { "revision": "3f2a9c41d07be512", "skills": [{ "name": "work-on-card", "audience": "agents", "revision": "9b1c…", "bundled": "same" }] },
"providers": [
{ "provider": "claude-code", "found": true, "version": "2.1.283", "source": "PATH", "updateAvailable": false, "loggedIn": true },
{ "provider": "codex", "found": true, "version": "0.159.0", "source": "PATH", "updateAvailable": false, "loggedIn": true }
],
"workspace": { "name": "work", "path": "D:\\work" },
"workspaces": [{ "name": "work", "path": "D:\\work" }],
"liveSessions": [{ "workspace": "work", "project": "api", "agent": "Agent 1", "provider": "claude-code", "sessionId": "6f1c…", "status": "working" }]
}
A skill's revision is a hash of its files' content. Hive keeps it between calls and hashes a skill again only once its files have changed (a file written, added, removed or renamed), so polling the status only lists the skills' folders and checks their files' sizes and times, and never gives an outdated revision. A skill's name, description and audience come from the first 64 KB of its SKILL.md. Hive uses the first 1000 entries of the workspace's skills folder. A skill whose header can't be read, or whose audience is unknown, has audience: "none" and a problem (nobody gets it). A skill too big to check (over 64 MB, 2000 files and folders, or folders 12 deep) has an empty revision and a problem saying which; one that can't be read has an empty revision.
A workspace's Hive Assistant is listed in liveSessions with "project": null and "agent": "Assistant". It isn't a project, so the project endpoints don't reach it.
GET /v1/docs/agent-api — this reference as the running Hive shipped it: { version, api, content } (content is the Markdown). For a client that needs more than /v1/status without guessing routes.
Workspace
GET /v1/workspace — { name, path, config } for the request's workspace, where config lists the MCP servers on for all projects (mcp.enabled), or null if no workspace is open.
GET /v1/workspaces — every open workspace (one per window): [{ name, path }].
Projects
GET /v1/projects — every project in the request's workspace; with several windows open and none named, every open workspace's projects. Each has workspace (its workspace's name). Projects hidden or removed from Hive (Project → Remove Project…) aren't listed and can't be named. ?view=short lists each project without its path, ids, sessions and settings: { name, workspace, active, branch, agents: [{ name, provider, status, branch, backgroundTasks, watching?, progress? }] } (progress: an open progress run in a few words, "e2e: 12 suites 4/12, about 6 min left").
GET /v1/projects/{name} — one project.
json{
"name": "api",
"path": "D:\\work\\api",
"active": true,
"branch": "main",
"status": "waiting",
"sessionId": "6f1c…",
"statusMessage": "Claude needs your permission to use Bash",
"restartNeeded": false,
"agents": [
{ "id": "a-3f9c01d2", "name": "Agent 1", "provider": "claude-code", "branch": null, "worktree": null, "status": "waiting", "sessionId": "6f1c…", "statusMessage": "Claude needs your permission to use Bash", "reviewing": null, "backgroundTasks": 0, "mcpServers": ["github"], "mcpChoice": "all", "launched": { "guidance": "3f2a9c41d07be512", "skills": { "work-on-card": "9b1c…" } } },
{ "id": "a-7b21e4aa", "name": "Reviewer", "provider": "codex", "branch": "hive/work/api/reviewer", "worktree": "D:\\work.worktrees\\api\\reviewer", "status": "stopped", "sessionId": null, "statusMessage": null, "reviewing": null, "backgroundTasks": 0, "mcpServers": [], "mcpChoice": "only", "launched": null }
],
"mcp": { "available": ["github", "godot"], "on": ["github"] },
"settings": { "model": "opus", "effort": "inherit", "permissionMode": "inherit", "chime": "inherit", "mcp": { "disabled": [], "enabled": ["github"] } }
}
status is one of stopped, starting, ready, working, waiting, background, watching, finished, error, signin. signin means its CLI refused the turn because the sign-in has expired (or an API key was refused): it waits for the user to sign in again, its statusMessage says so with the CLI's own words, and giving it a prompt is 409; once the CLI is signed in again it is ready. background means the agent's turn has ended but background tasks it started (a test run, say) are still running, and it carries on by itself when they end (Claude Code). watching means its turn has ended with a card watch running (POST /v1/tasks/wait with wake): Hive wakes it when a watched card changes; its statusMessage is what for ("Waiting for #12 → Review"), and it has watching ({ cards, column?, changes, label, limitAt }; short rows: the label). Each agent also has backgroundTasks, how many of those are running; Codex agents count theirs but stay finished, since Codex isn't told when they end. reviewing is an action the CLI's own automatic reviewer is checking while the agent works (Codex in Approve for me: "Codex asks to run …"), else null; nobody is asked, so it isn't a reason to tell the user. provider is the CLI the agent runs (claude-code, codex or copilot); each agent chooses its own, so a project can mix them. mcp is the workspace's MCP servers for the project: available (every server in the workspace, which any project can turn on) and on (those on in this project: turned on in it, or on for all projects and not turned off in it; settings.mcp has the project's own choices, enabled and disabled). mcpServers lists the project's MCP servers the agent gets (those on in the project; Hive's own hive server besides, always): all of them when mcpChoice is all (the default), or those of them it was given when only (its settings' MCP servers: Only these; the names chosen are its mcpServers in settings.agents). settings is the project's configuration, with per-provider overrides under providers. unusedWorktrees (only when there are any) counts the project's git worktrees no agent works in, kept when their agents were removed: { "count": 3, "merged": 2 } (merged: merged into the main branch and clean, so the user can remove them safely in the project's Overview). Removing them is the user's: there is no endpoint. A project has up to twelve agents (Settings → Advanced → Agents per project raises it, to 24 at most; adding one past it is refused, saying so), all equal, in the order they were added; a new project has none. The top-level status, sessionId and statusMessage are the first running agent's, and agents lists every agent. A running agent's launched is what it was given at launch: Hive's guidance revision, the content hash of each Hive skill's copy it reads, and problems for any skill it didn't get as asked ({ "handover": "a folder of the user's in .agents/skills is named hive-handover" }, or an old copy kept because it was in use), to compare with GET /v1/status (an agent launched by an older build, or before a skill changed, shows different ones); null when it isn't running. Agent ids are random (a-…) and never reused. Endpoints that act on a session take an optional agent — its id or name. Without one, the project's only agent is used; a project with several answers 400 (say which), one with none 409.
POST /v1/projects/{name}/activate — mark the project as being worked on.
POST /v1/projects/{name}/deactivate — mark it as not being worked on (fails with 409 while a session is running).
Sessions
GET /v1/projects/{name}/sessions — Hive and external sessions for the project, newest first, each with usage and a re-cache estimate. A session a CLI started for another one (a Codex guardian review) has sub: { parentId, kind }: it isn't a conversation, and resuming it is refused (409).
POST /v1/projects/{name}/sessions — start a session. Body (all optional):
json{ "resumeId": "6f1c…", "name": "Fix login bug", "agent": "Reviewer" }
Omit resumeId to start a new session. Returns the live session state (which includes agentId and cwd, the folder it runs in). Fails with 409 if the agent is already running or starting, if that conversation is already open in another agent, or if it ran in a different folder than the agent works in, or if the project has no agents yet (agents are added in Hive); with 400 if resumeId isn't a session ID.
POST /v1/projects/{name}/stop[?agent=…] — stop one agent's session, or every running agent of the project when agent is omitted.
GET /v1/projects/{name}/usage[?sessionId=…][&agent=…][&days=false] — usage for the agent's live session (see above for which agent), else the most recent Hive session, or the one given. days breaks it down by local calendar day (YYYY-MM-DD): tokens, requests, prompts, compactions and that day's API-equivalent cost (costUsd, costEstimated); days=false leaves it out (it grows with every day the session runs). Session lists include the same.
json{
"sessionId": "6f1c…",
"provider": "claude-code",
"title": "Fix login bug",
"model": "claude-opus-5-5",
"inputTokens": 1204,
"outputTokens": 48211,
"reasoningTokens": 0,
"cacheWriteTokens": 190233,
"cacheReadTokens": 2811093,
"contextTokens": 84211,
"requests": 57,
"compactions": [{ "timestamp": "…", "trigger": "auto", "preTokens": 968490, "postTokens": 14813 }],
"cacheTtlSeconds": 3600,
"contextWindow": null,
"costUsd": 4.82,
"costEstimated": false,
"lastActivity": "2026-09-28T12:30:00Z"
}
costUsd is the API-equivalent cost: reported by the provider (Claude Code), or estimated by Hive from its price table (costEstimated: true), or null for a model without a price. It is not what a subscription plan charges. reasoningTokens (Codex) are included in outputTokens; contextWindow is the model's window when the provider reports it (Codex always; Claude Code while the session is running).
POST /v1/projects/{name}/input — type into the running session. Disabled by default; enable Allow sending input to sessions in Settings → Agent API.
json{ "text": "Run the tests and fix any failures", "submit": true, "agent": "Agent 2" }
submit (default true) presses Enter after the text.
POST /v1/projects/{name}/handover — hand one agent's work over to another, which may use a different provider (Hand Over to… in Hive). Also needs Allow sending input to sessions, and Hive's tools in sessions.
json{ "from": "Agent 1", "to": "Reviewer", "handover": true }
With handover (the default), from must be running and idle: it is asked to write a handover with hive_create_handover, and Hive waits until a new handover for the project exists (not just for the agent to stop). Then to starts a new session (or, if it is running and idle, gets the message in its current one) and reads the latest handover. The call returns { "ok": true } at once; the handover can take minutes, and problems are shown as notifications in Hive. The new session's record has handedOverFrom, the session whose work it took over. With to the same as from, the agent carries on in a new conversation: once the handover exists, Hive stops it, starts a new conversation and gives it the handover, which is how a long transcript becomes short again.
Agents and providers
These read-only calls are open to every caller:
GET /v1/providers — the coding-agent providers: whether each is turned on and installed (enabled, installed, version, problem), whether it's the default, and the models, efforts and modes an agent can use, and whether its agents can take context200k. models are the ones the installed CLI reports (else the fallback list in Settings), and efforts the effort fallback list: a model may take fewer levels.
GET /v1/projects/{name}/agents/{agent}/activity[?detail=true] — what one agent is doing: status, statusMessage, reviewing and backgroundTasks, with detail=true runtime (the facts its status stands for, or null when it isn't running: lifecycle starting, running, signin or stopped; turn running, over, unknown or none; maintenance compacting or null; attention waiting, question or null; composer held (a line Hive typed it hasn't taken) or free; backgroundTasks; and silentMinutes while its state is unknown: a running turn Hive has heard nothing of for a while, with no sign of its end, which counts as busy and isn't given work until the user marks it idle), transcriptMB (its conversation's transcript, or null) and transcriptWarnMB (the size past which Hive flags it, or null), currentTask (the start of its last prompt, 300 characters), latestReply (the start of its latest reply, 500 characters), toolCalls (how many tool calls since that prompt) and recentTools (the last three, each summary up to 100 characters), clipped (only when a text was cut: currentTask and/or latestReply, its whole length in characters), lockedFiles (relative to its folder), its branch and worktree, its session, watching (only while it has a card watch: { cards, column?, changes, label, limitAt }), progress (only while it has an open progress run: see Progress below), userTypedSecondsAgo (when the user last typed in its terminal), and inbox (only when something was left for it: unread, overdue, and its latest deliveries, five or twenty with detail, as GET …/deliveries gives them, with more when there are older ones). detail=true gives the prompt and reply up to 2,000 and 3,000 characters and the last ten tool calls (200 characters each). Neither is the whole conversation: toolCalls and clipped say what was left out, and the API gives no more of another agent's conversation than this. {agent} is the agent's name or id.
POST /v1/agents/wait — waits until agents stop working (finished, idle, waiting for the user or stopped), or timeoutSeconds (5–600, default 300). An agent waiting on its background tasks (background) still counts as working, since it carries on when they end; "ignoreBackground": true stops waiting at the end of its turn instead. An agent with a card watch (watching: its turn has ended and it waits for cards to change, POST /v1/tasks/wait) doesn't count as working, and its statusMessage is the watch's label ("Waiting for #12 → Review"). One that needs the user to sign in again (signin) is waiting for the user. An agent just given a task counts as working until its CLI takes it: a prompt Hive typed (POST …/prompt) in the last 30 s, or the task it was started with (…/start, …/prompt with newConversation, a new agent's prompt), which its CLI takes only after it is ready, so a wait called in between doesn't answer "ready" before the task has begun. Without agents, it waits for every busy agent in the workspace. The reply, headers included, comes only when the wait ends: give your HTTP client a longer timeout than the wait (Node's fetch stops waiting for headers after 300 s).
With "wake": true it is an agent watch (#416), for an agent's or the Assistant's own token: it replies at once, { "watching": "Waiting for B6 to finish", "limitAt": "…" } (with replaced, the label of a card watch it replaced), or { "already": ["B6 (hive) is idle: \"…\""] } and no watch when an agent named isn't working now. Hive then gives the caller a wake (a delivery it reads with hive_inbox, rung by the doorbell, #650) when a watched agent finishes its turn (with ignoreBackground, also while its background tasks still run), waits for the user, or stops, naming every agent that did and the start of its latest reply ([Hive] B6 (hive) finished: "The installer is built…". Your agent watch has ended: carry on …), or when limitMinutes (1–1440, default 120) pass with none. The line is delivered as a card watch's is (only while the caller is idle and the user isn't typing there; Enter again once if its CLI didn't take it), and the caller shows watching meanwhile. It is the caller's one watch: a card watch replaces it, and it replaces one. "cancel": true ends it. Without agents it watches every agent working now (already is ["No agent is working"] when none is), at most 32 (a project's most, 24, and more), never the caller itself (400). In both forms, a prompt Hive has just typed into an agent that its CLI hasn't taken yet counts as working, so a wait right after hive_prompt_agent waits for that task. A project agent may watch any project's agents (their status is open to it), but its line leaves out the reply of another project's agent.
The user's replies (#418): the Hive Assistant's watches (not an agent's) are also woken when the user sends an agent a prompt from its pane, telling the first line (120 characters): an agent watch on that agent ([Hive] User replied to B1 (hive): "C, the hive cell"…) and a card watch on a card that agent has or reviews ([Hive] User replied to B1 on #399: "C, the hive cell"…); with a name in Settings → General → Your name, that name instead of User ([Hive] Darren replied to B1…), in one line with whatever else changed. A prompt counts as the user's when its CLI takes it soon after the user's own Enter in that pane, later than any line Hive typed there (a task, a wake). A watch started after a wake counts a reply in between; one already told isn't told again. Each reply is told once, with the cards it concerns, and fitted with the rest within the line's size. Nothing is kept or told with Settings → Assistant → Tell the Assistant when I reply to an agent off, also a reply already waiting in a wake (on by default; the Assistant can't change it).
json{ "agents": [{ "project": "web", "agent": "Agent 2" }], "timeoutSeconds": 120 }
Returns { "timedOut": false, "waitedSeconds": 41, "agents": [{ "project": "web", "agent": "Agent 2", "status": "finished", "statusMessage": null, "backgroundTasks": 0 }] }.
Deliveries and the inbox
Everything Hive has for an agent to read is a delivery (#647): a record with an id (m12, unique in its project), its target agent and conversation, its kind (task, rebrief, card-event, answer, handover, control), who it is from, its card, what it says and its state. It is saved in its project's .hive/deliveries.json (the Assistant's in its home's) and restored when Hive starts. Every way Hive has into an agent makes one (#649): hive_message_agent (rebrief), hive_prompt_agent, hive_start_task and starting an agent with a prompt (task), card, lane and agent watches and loop wakes (card-event), the user's decisions (answer), handovers (handover), and the Assistant's mode notices, a lock's go-ahead and the carry-on after a sign-in (control).
States: queued (waiting to be offered), offered (Hive typed its doorbell into the agent's terminal, one short line naming it and what else waited by id, never what they say: [Hive] New in your inbox: 1 message (m12). Call hive_inbox to read it.), received (the agent read it with hive_inbox, receivedVia: "inbox": the only receipt, #650; receivedVia: "typed", its CLI taking a line that carried its whole text, is only in records from before), acked (the agent says it acted on it); or ended without that: cancelled (by whom), refused (its sender may no longer send it: Settings → Assistant → Control turned down, or the Assistant's launch that left it has ended, whose refusal is also in the Assistant's activity list), expired (not received within 24 hours, its agent is in another conversation now, or the launch it was for has ended), stale (#652: tied to a card round, card and round on the delivery, and that card has moved on: passed, done or archived, out of Doing and Review, or another review failed; detail says which, #699 passed review, and the Assistant's activity list records one it sent). uncertain: offered when Hive stopped, or when its agent's launch ended before it was read: it may not have reached the agent, and is offered once more, its doorbell saying so ((m12 again: Hive restarted while offering it, so it may repeat.)), then left uncertain. A doorbell the agent's CLI took, with the agent idle again without reading it, rings once more, never a third time; once typed, a doorbell is never cleared or taken back (#652), and what it names that has ended meanwhile is simply not given when the agent reads its inbox. One that no longer holds as it was offered (what it was made from changed) is made again when it is read. A card-event or control delivery is done once received; the other kinds wait for the agent's ack, once it has read them (an id alone, as a doorbell names it, acks nothing). A project agent with no inbox (the Agent API or its built-in hive server off) is given no delivery: every one is refused with the reason ("B1 has no inbox: turn on the Agent API"), never typed instead; only the user's own Start from Hive is typed as their input. One of those not acked 30 minutes after it was made is overdue (overdue: true), shown in the agent's pane too. A sender's permission is worked out again from the saved sender and the settings at every step; no token is saved.
POST /v1/inbox — the calling agent's (or the Assistant's) own inbox: { items, unacked }, items being its deliveries not received yet, oldest first, each { ids, kind, from, card?, text, at } (a card's events coalesce into one item, "3 changes, read the card"), now received; unacked the ids received before and not acked. { "ack": ["m12", …] } acks those (at most 200) and replies { acked, unknown, unread }: only what the caller has read (#650); one not read yet is unread, unchanged (read it first); an id that isn't the caller's, or one cancelled, refused or expired, is unknown, like one that doesn't exist; acked again, it is still acked. A change that couldn't be saved fails (500) and isn't made. Other callers get 400.
GET /v1/projects/{name}/agents/{agent}/deliveries[?limit=n] — an agent's deliveries: { unread, overdue, deliveries, more? }, the newest limit (20 by default, at most 200), each { id, kind, from, card?, text, state, created, offered?, received?, receivedVia?, acked?, ended?, detail?, overdue? } (text the start of what it says, 300 characters). A project agent reads its own project's only.
The Hive Assistant
Each workspace's Hive Assistant calls the API with its own token, a new one each time it starts, in a file its hive MCP server reads. Hive applies Settings → Assistant → Control to its calls instead of Settings → Agent API, and they always concern its own workspace (a request naming another workspace or one of its projects gets 404). With the Agent API turned off, the server still runs and answers the Assistant alone; other callers get 403.
The changes below are the Assistant's only: other callers get 403.
| Call | Needs | What it does |
|---|---|---|
POST / { "name" } |
Control agents and create projects | Creates a project folder in the workspace and turns it on |
POST / |
Control agents | Turns a project on (other callers: no restriction, as before) |
POST / |
Control agents | Adds an agent: name, provider, model, effort, mode, context200k ("on" or "off": a 200K context window instead of the model's 1M, for providers with context200k), worktree (with branch, by default hive/, and base: an unused worktree already on that branch, merged into the base and clean, is reused, brought up to the base; one holding work is left as it is and a new one made, and the reply says which), or existingWorktree (an unused worktree's folder or branch, 404 if there is none: one merged and clean is brought up to the project folder's branch, any other is taken with its work), mcpServers (only these of the project's MCP servers, by name; [] none; leave it out for all of them); start starts it, and prompt starts it on that task, given on the CLI's command line |
PATCH / |
Control agents | Changes name, provider, model, effort, mode, context200k (empty clears an override) or mcpServers (only these of the project's MCP servers, [] none, ["*"] all of them again; the hive server can't be named: it is always given) |
POST / |
Control agents | Starts a stopped agent: a new conversation, or resume: true (its last) or a session id; prompt as above |
POST / |
Control agents | Stops it. If it is working, waiting or starting, Hive asks the user in the Assistant's panel (with the optional reason) and the call waits for the answer: 409 if they say no |
POST / { "text", "newConversation"? } |
Control agents | Gives an idle agent a task now: a task delivery (#649) given now or not at all, never queued. Its doorbell is typed (#650: its id, never its text); the agent reads the task with hive_inbox (received) and acks it once it has acted on it. To an agent with no inbox (the Agent API or its hive server off) only the user's own task goes, typed as their input; anyone else's is refused ("… has no inbox: turn on the Agent API"). Replies { ok, id } (no id for a task typed as the user's). 409 while it is working, starting, waiting on its background tasks or waiting for the user, or when the user has just typed in its terminal (Settings → Assistant → Pause after you type). "newConversation": true (#437): the agent's conversation ends (it stays in the Sessions tab) and it starts in a new one on the task, as Hand Over to… itself does but with no handover; the same 409s, and also while Hive is typing into it. If Settings → Assistant → Control is turned down, or the Assistant's session that asked ends, while the agent stops, no new conversation starts (403/409, saying the old one has ended). Replies { "ok": true, "newConversation": true } |
POST / { "text", "card"? } or { "cancel": true } |
Control agents | Leaves a running agent a message (#530) when it can't take a prompt now: a delivery (#647, see Deliveries and the inbox), saved, and the agent reads it with hive_inbox (#650: never typed in). Its doorbell is typed at once when the agent is idle, watching, or has ended its turn while its own background tasks run (background, #726) (a watching agent is woken by it, its watch carries on, and the line says so: [Hive] New in your inbox: 1 message (m4). Call hive_inbox to read it. (Your watch is still on, …)); a busy one is told at its next tool call or before its turn ends, else it waits for its next line: with a wake that comes first (a card or agent watch's, a loop wake), or a line of its own once it is idle, nobody types there and no earlier line waits. Several waiting go in one line. Each keeps the Assistant's permission until it is read: with Control turned down or the Assistant's launch that left it ended before then, it is refused, never given. card (#652): tied to that card's round under way (its failed reviews plus one; 409 unless the card is in Doing or Review), it is stale, never given, once the card has moved on (passed, out of Doing and Review, or another review failed), even if its doorbell has rung. At most 5 waiting, 4000 characters each (400/409 past that); 409 for an agent not running. Kept across the agent's stops and Hive's restarts (refused once the Assistant's launch has ended). Replies { "id", "typed", "queued", "status", "round"? } (typed: its doorbell was typed; round: the card round it is tied to), or 409 when it was cancelled, refused, expired or stale before the reply; cancel cancels those not read yet (its hive_inbox no longer gives them), and replies { "cancelled": n }. The agent's activity has inbox with each one's state and times |
POST / |
Control agents | Hand Over to… (below), without needing Allow sending input to sessions. 409 at once when the project's agents lack Hive's tools, the user has just typed in either agent's terminal, or (without a new handover) the project has no handover; later failures are listed in its actions |
POST / |
Control agents | Starts a card on an agent (see Task board) |
Changing the task board (POST /v1/tasks, PATCH /v1/tasks/{n}, comments) is open to other callers too; for the Assistant it needs Control agents and counts as a change.
With Look and advise these return 403. The Assistant can make 30 changes for each message from the user; then 429. A batch of cards is one change, however many cards it has. Every change, and every refusal, is listed in the Assistant's panel and in hive.log. There is no call to remove agents, discard worktrees, archive or delete cards, or remove projects. For the Assistant, POST /v1/projects/{name}/sessions, /stop and /input answer 400 (it uses the calls above), and /deactivate needs Control agents.
Settings
Hive's settings, as Settings and Project Settings describe them (one catalog of every setting: src/shared/settingsCatalog.ts). Anyone with a token can read them; only the Hive Assistant changes them.
GET /v1/settings — every setting a row: id, title, value (as shown: on/off, a number, inherit, (empty)), default (only when the value differs), desc (its first sentence) and readOnly: true for those only the user changes. query keeps those whose id, place in Settings or text has every word; scope one of app, provider, workspace (no value settings yet) or project; project=<name> adds that project's own settings (ids project.…, and project.<provider>.…), which need it. Buttons, statuses and lists (Reset, the Agent API's token, Hidden projects…) aren't settings and aren't listed.
GET /v1/settings/{id} — one in full: path ("Settings → Sessions → Warn when a transcript is over"), shownAs (where the user sees it, in the language Hive shows, only when that isn't English: the rest stays English), scope, value, default, takes (its range, options or type), desc, tip, helps (when it is worth suggesting), restart (when a change applies, if not at once), readOnly (why the Assistant can't change it), docs (the user guide's heading). A table's value is its JSON (cut at 3,000 characters); in the listing it is its size. The Assistant's own settings with each provider are entries of their own (assistant.<provider>.model, .effort, .permissionMode, .extraArgs, .use200kContext): read-only to it. A project's setting takes ?project=<name>.
PATCH /v1/settings/{id} { "value", "project"? } — the Hive Assistant only (others: 403), and only with Settings → Assistant → Control → Change settings on (403 otherwise, at once when the user turns it off). The value is checked as Settings checks it (400 with the range or options), and the change is made as Settings makes it. Never for a sensitive setting (403): permission modes, the Agent API, what Hive runs (CLI paths, extra arguments, a project's setup command, background sessions, pre-release updates) and the Assistant's own settings (Control above all). A project's number or list takes null to inherit Hive's. An effort level must be one the effort picker offers for the model that would run (takes lists them), or "" (the default; inherit for a project). Tables take JSON: board.colors an object of column → #rrggbb (only those named change), <provider>.prices the whole table of model → {input, cachedInput, output, cacheWrite?}, <provider>.modelFallback and .effortFallback the whole list of {value, label} (older for models); null puts back Hive's (for prices, as Reset to defaults does: the shipped models removed from the table come back too, and the change lists them). Shortcuts are read-only for now. The permission, and that the request's Assistant session (its launch's token) is still the running one, are checked again at the moment of the change, under the project's lock for a project's setting (for Revert, also that the value is still what the change set). The reply's changed says whether anything changed; for a table, old and new are the entries that changed. Replies with old and new (as shown) and restart when it applies later. It counts as one of the Assistant's changes, and its panel lists it with old → new and a Revert button (which the user, not the API, uses).
Shared notes
Shared notes live in the workspace's .hive/shared folder. Paths are relative to that folder and cannot escape it.
GET /v1/shared — the notes tree.
GET /v1/shared/file?path=handovers/2026-09-28-api-auth.md — { path, content, revision }. The revision is a short hash of the note's text.
GET /v1/shared/handovers?project=api — the project's handovers, newest first, as [{ path, modified }] (paths as /v1/shared/file takes them; which handovers are a project's is below, under the hive tools). Without project, every handover. An unknown project is 404.
PUT /v1/shared/file?path=conventions.md — create or replace a note:
json{ "content": "# Conventions\n…", "append": false, "expectedRevision": "3f9a0c1d2e4b" }
Set "append": true to add to the end of an existing note. expectedRevision is optional: with it, the note is written only if it is still at that revision (checked and written under one lock, so two writers that read the same text can't both pass); otherwise nothing is written and the reply is 409 with { error, revision }, the note's current revision (null if it no longer exists): read it again, merge, and retry. Without it, the note is written as before; given but not a revision (empty, null, not a string), it is refused with 400 and nothing is written. The reply is { ok, path, revision }, the note's new revision.
POST /v1/shared/handovers — write a dated handover note into shared/handovers/:
json{ "project": "api", "title": "Auth refactor", "content": "## State\n…\n## Next steps\n…" }
Returns { ok: true, path }. Hive shows a notification with a link to the note. Hive writes the note's header (# <title>, project and date, in local time with UTC in brackets). An agent's hive_create_handover also passes agent (its id) and agentProject, and the Hive Assistant is known by its token; the header then adds - **Author:** <agent> (<CLI>) (or Assistant) and - **Session:** <id>, the conversation it was written in.
Task board
The workspace's board: cards in six columns, hold (On Hold), todo, doing, review, passed and done, kept in .hive/tasks. hold is parked work, passed is work whose review passed and waits to be merged, and done means merged. Callers can read and change cards and move them between the columns, done included; each move is in the card's history with who made it. Putting a card in hold (moving it there, or creating it there) is the user's, the Assistant's and the workspace token's: a project agent's own token gets 403; it may move a card out of hold. Agents are asked (their session contract and the skills) to move a card they were given to doing before they start on it (also one back from review, passed or done with more work), to move finished work to review with a comment saying what they did (also a card that was in passed or done), as a reviewer to move a card that passes to passed with the verdict, to move a card to done once its work is merged or when the user asks, and to leave hold to the user. Creating a card in done and putting done in order are the user's (403). Archived cards can't be changed (403), and archiving and deleting are only in Hive: a PATCH with archived is refused (403), and there is no call to archive cards in bulk.
Who sees what. The workspace token (scripts) and the Hive Assistant see and change the whole board, as the user does. A project agent, calling with its own token (HIVE_API_TOKEN), sees and changes only its project's cards; who it is comes from the token alone, never from the request:
GET /v1/taskslists its project's cards; asking for another project (?project=) is403.- Another project's card, or one with no project, answers as if it didn't exist: reading, changing, commenting on or moving it is
404(Unknown task #63), and naming it inblockedBy,links,beforeor a reorder is400(there is no card #63). - A new card is its project's (also when
projectis left out); another project, or"", is403, and so is changing a card'sproject. - A card of its own that waits for or links to another project's card shows that card as a number, listed in
elsewhere; changingblockedByorlinkskeeps those. positionand a reorder place its cards among its project's cards in the column (the top or bottom of those); other projects' cards keep their places. The place a short reply gives (3rd of 5) counts its project's cards.- Another project's agents' conversations hold that project's cards (a card started on an agent is its prompt), so for another project,
GET /v1/projects/{name}/agents/{agent}/activity,GET /v1/projects/{name}/sessions, andPOST /v1/projects/{name}/inputare403, and the event stream (/v1/events) leaves out their session events. Their status (/v1/projects,/v1/projects/{name},/v1/agents/wait) stays open; an agent watch's line leaves out what another project's agent said.
An agent runs as the user, so it could read the workspace token from disk: this keeps agents to their own project's work rather than containing a hostile one.
GET /v1/tasks[?project=web][&column=review][&archived=true] — the cards on the board in order (by column, then position), or the archived ones. A card:
json{
"number": 12, "title": "Fix the login redirect",
"decisions": [{ "text": "Keep the old URL.", "decidedBy": "user", "recordedBy": "Assistant", "at": "…" }],
"description": "…markdown…",
"project": "web", "column": "doing", "order": 3, "labels": ["bug"],
"blocked": null, "blockedBy": [11], "links": [],
"agent": { "id": "a-3f9c01d2", "name": "Agent 1", "status": "working", "backgroundTasks": 0 },
"agentName": "Agent 1",
"comments": [{ "at": "…", "by": "Agent 1 (web)", "text": "Found it: the callback URL." }],
"history": [{ "at": "…", "by": "Assistant", "what": "Moved to Doing" }],
"archived": false, "createdAt": "…", "createdBy": "Assistant", "updatedAt": "…",
"stalled": null
}
agent is the agent the card is given to, with what it is doing now (status is removed if the agent no longer exists), or null. stalled says why nobody is working on a card in doing ("Agent 1 isn't running.", "Agent 2 was removed.", or that no agent has it), or that an agent in a card loop left a card in doing or review with no card watch ("Reviewer isn't watching #12: its turn ended with no card watch."), or that a card has waited in review for 15 minutes, unreviewed, while an agent watches it ("Reviewer is watching #12 but hasn't started reviewing it: in Review for 40 min."), or that its reviewer started reviewing it and has only been watching it for 15 minutes, with no verdict and the card not blocked ("Reviewer started reviewing #12 but has given no verdict, and has only been watching it for 20 min."), and is null otherwise; an agent that has finished its turn doesn't otherwise make its card stalled. project is the project's folder name, or "" for a card about the workspace. by and createdBy say who: You, Assistant, an agent ("Agent 1 (web)", calling with its own token) or Agent API (the workspace token).
GET /v1/tasks/{n}[?history=false][&comments=n] — one card (#12 or 12). history=false leaves out its history and gives historyEntries, how many entries it has; the description and comments stay. comments=n (a whole number from 1) keeps only the newest n comments and adds commentsOmitted, how many earlier ones there are: a long card's latest feedback without its whole thread.
GET /v1/tasks/{n}/comments/latest — only the card's newest comment, for "check the latest comment" without the whole card: { "number": 12, "comment": { "at": "…", "by": "Codex (web)", "text": "…the whole comment…" } }, or "comment": null when it has none. The newest is the last one added (comments are kept in the order they were added, so equal times don't matter). The description, earlier comments and history are left out. Who may read it is as for the card (404 for a card a project agent can't see, as for a missing one). The hive tools' hive_read_task gives it with latestComment: true.
?summary=true (#532), on this and on GET /v1/tasks/{n}: each comment as its summary, its first line that isn't blank (at most 300 characters), with "length", the whole comment's in characters, when there is more: { "at": "…", "by": "HR1 (hive)", "text": "Round 1: FAILED: 2 findings: …", "length": 4210 }. Reports and verdicts lead with a one-line summary, so a reader that needs only the gist reads that and asks for the whole when it acts on it. hive_read_task gives it with summary: true.
POST /v1/tasks — add a card. title is required; description, project, agent (name or id, in that project), column (not done; not hold for a project agent), labels, blocked (a reason), blockedBy and links (card numbers) are optional. Created in doing by an agent without agent, it is given to that agent.
json{ "title": "Add tests for the redirect", "project": "web", "description": "…", "labels": ["tests"], "blockedBy": [12] }
PATCH /v1/tasks/{n} — change a card and/or comment on it: any of title, description, project (a card that changes project always leaves its agent, and the history says whose it was; naming an agent in the same change is 400: give it to one of the new project's agents in a change of its own), agent (empty takes it from its agent), column, position (top or bottom of its column) or before (the card it goes in front of, which has to be in the column the card ends up in; null the end), labels, blocked (empty clears it), blockedBy, links, decision (records a decision the user made about the card: it is attributed to the user, with the caller as who recorded it; only the user changes or removes decisions, in Hive), and comment. A card's decisions come first in its JSON (absent when it has none). A project agent's replies to any call carry an X-Hive-Notice header (URL-encoded) when a card it works on (in doing, its agent), reviews, or reviewed before and is back in review (or such a card when the call began, unless the call read it: moving a card to review still hears of one) has a decision recorded by someone else, or changed by the user, since it last read the card in full (GET /v1/tasks/{n}), else since its work began or its last verdict on it: "[Hive] #12 has 1 new decision since you last read it: …". The hive tools add it to their reply as a last line. When an agent moves a card into doing from another column without agent, it is given to that agent (the history says "Given to …"), also when another agent had it; a card already in doing keeps its agent. An explicit agent, including empty, wins, and the Assistant and other callers give it to nobody. A project agent (its own token) can't move a card that is in doing with a different agent to review or done: 409 ("… is in Doing with Alma, who is working on it: newer work is in progress …"), checked against the card as it was, so an agent in the same change doesn't get round it. Its own agent, the Assistant and the workspace token can.
Reviewing a card: review in the body. "start" marks the card as being reviewed by the calling agent (it has to be in review, and review: "start" goes on its own, without column or agent): the card keeps its agent and gains review ({ "agent", "agentName", "since" }). "passed" or "failed" ends it, with the verdict as the comment; "passed" with "column": "passed" moves it to Passed as well (what reviewers do), or with "column": "done" to Done; "failed" with any column but review is 400 (a failed card stays in Review). Only a project agent, with its own token, reviews its project's cards (403 for others); another agent's start while one is reviewing is 400, naming it, and only the reviewer gives the verdict, while its review is going: a verdict after the review ended (the card left review, or the reviewer's session ended) is 400, so start a new review first. The review stops when the card leaves review or its project, and when the reviewer's session ends or it is removed. A "failed" verdict leaves the card in review: its own agent, with its own token, sending "column": "review" returns it for review (history "Returned for review, round N"), which wakes a wait for it to move into review; once per failed verdict, and any other move to the column a card is in is no change. Card views add reviewStalled while a review goes on but its reviewer has gone or isn't running; ?view=short rows add reviewing ({ "name", "stalled"? }).
json{ "column": "review", "comment": "Fixed in auth/callback.ts; tests pass." }
Placing a card with position or before adds a line to its history when it moves it. A before card in another column, or both position and before, is 400; placing a card in Done is 403 (the order of Done is the user's).
GET /v1/tasks returns cards in board order: by column, then top first. That order is their priority.
POST /v1/tasks/reorder — put cards in order: column (hold, todo, doing, review or passed) and cards, card numbers highest priority first. The listed cards go to the top of the column in that order; the column's other cards keep their order below them. Every listed card has to be in that column already (move it first with PATCH), or nothing changes and the answer is 400; done is 403. Returns the column's cards in their new order.
json{ "column": "todo", "cards": [14, 9, 12] }
POST /v1/tasks/batch — change up to 100 cards with one call: numbers (their card numbers) and the fields every one of them gets, with the meaning each has on PATCH: column, position (top or bottom), blocked, labels or agent. A title, comment, decision or link goes on PATCH for its own card; any other field here is 400. An empty list, more than 100 cards, or a card listed twice is 400, and nothing changes.
Each card is changed as PATCH would change it: under its own lock, authorised against the card as it is then, with its own history line. A card that can't be changed is refused and left as it was, and the other cards still change: an unknown card, or another project's (both answered as missing), an archived one, one in Doing with another agent, one an agent may not put On Hold. So a batch is not all or nothing: each card is. position applies to each card in turn, so the last listed ends at the top; to set an order, use POST /v1/tasks/reorder.
For the Assistant, the batch counts as one of its 30 changes for the message, however many cards it has. Once those are used, the batch changes none of its cards and the answer is 429 with fits: 0. Each card is listed in the Assistant's panel on its own. Each card is checked again just before it is saved, after its last read and under its lock: if the control level was turned down, or the Assistant's session replaced, while the batch was working on it, that card and the rest after it are refused and nothing of theirs is saved.
json{ "numbers": [14, 9, 12], "column": "todo", "reply": "short" }
Short replies. Every caller gets the replies above unless it asks for short ones, which is what Hive's own tools do (an agent pays for each character of a reply on every later turn):
GET /v1/tasks?view=short— a row per card:{ number, title, column, project, agent: { name, status, backgroundTasks } | null, labels, blocked, blockedBy, stalled, reviewing?, comments (how many), archived }, without descriptions, comments or history. (hive_list_tasksshows at most 200 of these a reply, with theoffsetto carry on.)"reply": "short"in the body ofPOST /v1/tasks,PATCH /v1/tasks/{n}andPOST /v1/tasks/{n}/comments— what changed and where the card is now, instead of the card:{ number, title, column, position (1 at the top; null when archived), of (cards in the column), project, agent (its name or null), changes }.changesis in the words of the card's history ("Moved to Review","Commented"; empty when nothing changed)."reply": "short"inPOST /v1/tasks/reorder—{ column, top (the cards put at the top, in order), count (cards in the column) }instead of the column."reply": "short"inPOST /v1/tasks/batch—{ changed (each card's change, asPATCH's short reply gives it), refused ({ number, error }for each card left as it was) }."reply": "short"inPOST /v1/tasks/{n}/start— itscardas a short row.
POST /v1/tasks/{n}/comments { "text" } — add a comment.
POST /v1/tasks/wait — wait for cards to change. cards (1–20 card numbers the caller can see; others are 404), changes (any of column, comment, verdict, agent; default all), column (hold, todo, doing, review, passed, done): alone, until a card is in that column (at once if it already is), and nothing else counts; with changes naming column, until one moves into it or another change listed ("changes": ["verdict", "column"], "column": "passed": a verdict, or into Passed). Either way a card already in that column answers at once (#434). "fresh": true (with column) waits only for the next move into it: a card already there waits until it leaves and comes back, or is returned for review (a reviewer waiting for a failed card's next round); fresh without column is 400. column with changes that don't name it is 400. A card that leaves the caller's view (another project, for a project agent), is archived or is deleted is reported as gone ("column": "gone", "changes": "gone"), with nothing of its state but "archived": true when it was archived (a wake line says "#12 was archived").
- Bounded (default): waits up to
timeoutSeconds(default 300, at most 840) and returns{ "changes": [{ "number": 12, "column": "review", "changes": ["column", "comment"], "by": "Claudette (web)", "comment": { "by": "Claudette (web)", "firstLine": "Fixed the redirect." } }], "timedOut": false, "since": "…" }, or{ "timedOut": true, "since": "…" }.byis who made the card's latest change; Passsinceback to the next wait to measure from where this one ended, so nothing between them is missed. At most two at once per caller (429);409if the workspace closes meanwhile. As with/v1/agents/wait, the reply's headers come only when the wait ends, so your client's timeout must be longer than the wait. "wake": true(an agent or the Assistant, with its own token;400for the workspace token): starts a card watch for the caller and answers at once with{ "watching": "Waiting for #12 → Review", "limitAt": "…" }, or{ "already": { …a change… } }when acolumncondition is already met. The agent ends its turn; Hive wakes it when a card changes (a delivery it reads withhive_inbox, rung by the doorbell, #650:[Hive] #12 is in Review; latest comment by …), or afterlimitMinutes(1–1440, default 120) with no change ([Hive] No change on #12 in 2 h: …); the watch ends once the agent has read that wake withhive_inbox, not when its doorbell is typed (the only line Hive types: fixed words and the wake's id). The wake names every watched card that changed (when they don't all fit in full, briefly or by number, saying details were left out): it is made 1.5 s after the first change and tells the cards as they are then ([Hive] #217 is in Review: Codex (hive) passed it (…); #119 is in Review: Codex (hive) failed it (…). …). The caller's next watch counts what others changed after its last wake (not what it did itself), so a change between a wake and the next watch isn't missed. A new watch replaces the agent's previous one (analreadyanswer ends it too). The watch keeps the agent's view of the board: a project agent's project, the Assistant's whole board. Watches are kept in.hive/watches.json, and a stopped agent's is delivered when it is resumed. A watch that can't be saved is an error, not a watch; a workspace keeps at most 500 (409past that; replacing an agent's own is always allowed). The Assistant's card watch also counts the user's replies to its cards' agents and reviewers (see The user's replies under/v1/agents/wait).- A card leaves a project agent's watch when the agent's part in it ends (#420): given to another agent while the watcher had it, reviewed by another agent while the watcher reviewed or awaited it (a
column: reviewwatch on another agent's card). A blocked card stays in the watch: it is paused, not gone (#481). The watch's only card wakes it once ([Hive] #354 was reassigned to B4 (your watch on it ended). Your card watch has ended: drop it from your list and carry on with your next card.); otherwise the card is dropped and told in the watch's next line. Noted in the card's history as Hive. A card the watcher has no part in, and the Assistant's watches, are never released. "lanes"(#531, with"wake": true, withoutcolumnandfresh): a lane watch on lane ids (GET /v1/lanes; one not in the caller's view is404) or["all"], every running lane in the caller's view, started later too, each lane's cards followed as it goes on, with no card limit."events"(defaultfailed,blocked,stalled,merge-due,ended,abandoned,waiting; alsopassed,moved) is what counts.stalledis a card that stalls, or a lane's merge (#869): every card passed, none blocked, and its builder not running, or idle (its turn ended, no background task) with no place at the merge slot, for 10 minutes.waiting(#726): an agent of the projects it follows (the caller's view; named lanes: their projects) starts waiting for the user (the statuswaiting: a permission prompt or a question in its pane), told once a wait while it still waits."cards"(#726): up to 20 cards beside the lanes, followed in the same watch as a card watch follows them,"changes"saying what counts (defaultcomment,column,verdict) and a card becoming blocked counting too; left out, the caller's last lane watch's cards are kept (less those since done or gone),[]drops them. Replies{ "watching": "Waiting on all running lanes and #720", "limitAt", "events", "replaced"?, "cards"?: { "now", "kept"?, "replaced"?, "dropped"? } }; the wake line names each lane and what happened in it ("#12 failed review (R1 (alpha))", "#15 is blocked: …", "all its cards passed: its merge is due", "its merge is stalled: …", "ended (merged)"), then each card that changed ("#720 is in Review and blocked: …") and each agent waiting ("HB6 (hive) is waiting for the user on #720: …"). A watch after a wake counts what happened since that wake."cancel": true: ends the caller's watch:{ "done": "Cancelled your card watch." }(or lane, agent watch) or"You had no card watch.".
While an agent watches, its status is watching (below), POST …/agents/{agent}/prompt is 409, and starting a card on it is refused; POST …/agents/{agent}/message reaches it (#530).
POST /v1/tasks/{n}/start — the Hive Assistant only (Control agents): gives the card to an agent of its project, with the card as its prompt, and moves it to doing. The prompt is the card's own words (title, note, description, the cards it depends on) and, when the agent has Hive's tools, a pointer to the work-on-card skill and how many comments to read. A card in review or done can be started again for more work; the prompt says it is back and carries its latest comment (the feedback it came back with). note: what to do now (up to 4000 characters, such as "address the latest review comment"), added to the prompt before the card. If the card is moved to done while the start is under way, it stays there and the start fails. agent (name or id): an existing agent that is stopped (a new conversation) or idle (its next message); 409 if it is busy. Without agent, Hive adds one: name, provider, and worktree: true for its own git worktree. Returns { ok, agent, added, card }.
Merge slot
One merge at a time into a project's branch, so the branch can't move while an agent checks and merges (the merge-ready skill says when to take it). A slot is per project and branch; branch defaults to the branch the project folder is on (what Hive's Merge dialog merges into). Only a running agent of the project, with its own token, can claim or release its slot. Scripts and the Assistant can read it. An agent asking about another project gets 403.
GET /v1/projects/{name}/merge-slot?branch=main — the slot: { project, branch, holder, waiting }. holder is null when the slot is free, else { kind: "agent" | "user", name, cards, since, until, taken } (times in epoch ms; taken is false while it is an agent's turn that hasn't claimed it again yet). waiting lists who waits, in order.
POST /v1/projects/{name}/merge-slot/claim — { "branch": "main", "cards": [305], "timeoutSeconds": 240 } (all optional; 240 by default, at most 290, under the 300 seconds after which Node's fetch stops waiting for a reply). It waits in the call until the agent holds the slot, then replies { held: true, until, extended, slot }. On timeout the reply is { held: false, position, holder, slot }: the agent keeps its place for 2 minutes, so claiming again carries on in line. Claiming while holding extends the hold. A hold lasts 60 minutes; a Progress run the holder keeps reporting to (/v1/progress) keeps it longer. A hold that runs out goes to the next in line and is reported to the user. An agent whose hold ended without its release (it expired, or the user released it) gets lost: { branch, why, at } in its next reply.
POST /v1/projects/{name}/merge-slot/release — { "branch": "main" }: { released: true, next } (who has it now), { left: true } (it was waiting, and has left the line) or { none: true, holder }.
A slot is released when its agent's session ends. Hive's Merge dialog takes the slot for the length of the user's merge, and waits while an agent holds or waits for it.
Lanes
A lane is Hive's record of a card loop: a builder and a reviewer taking a project's cards in order (the card-loop skill registers it). It is bookkeeping, never a gate: nothing waits on a lane, and a loop works the same without one. A project agent starts its own lane, as its builder, joins its builder's lane, as its reviewer, and sees its own project's lanes; the Hive Assistant (Control agents) and scripts start lanes for any builder and see them all.
A lane is { id, project, name, builder: { id, name }, reviewer, cards, current, state, pausedOn, rounds, waitMinutes, startedBy, startedAt, updatedAt, endedAt, mergeCommit }:
cardsare in the lane's order, each{ number, title, column, blocked, round, reviewing }:columnisgonefor a card archived, deleted or moved to another project, androundis 1 plus the reviews it has failed.currentis the card being worked: the first in Doing that isn't blocked, else the first in Review, else the first not in Passed or Done.stateis one of:active;pausedwhile it waits for the user: its first card not in Passed or Done is blocked and no other card is in Doing or Review (pausedOn); back toactivewhen that card isn't blocked or another one is worked;endedonce every card is in Done, or when the builder ends it after its merge;abandonedwhen its builder or reviewer is removed from the project, or it is ended unmerged.
- An
endedorabandonedlane'scards(andcurrent) are as they were when it ended, not as the board has them now: another lane may be working those cards. A lane that ended without a record of them (before Hive kept one, or a damaged record) gives each card'scolumnasunrecorded(not known) andcurrentas null. - An agent that is stopped keeps its lane: quitting Hive and restarts stop agents too.
- A builder has one running lane: starting a lane that shares a card with its running one updates that lane (a card added, the order changed, a reviewer given), and one sharing none ends it (
endedwhen it has no card left to do, every one Passed, Done or gone, elseabandoned). - Ended lanes are listed until their builder starts another, or for an hour, and kept for a day.
GET /v1/lanes — the lanes running or recently ended, oldest first (?project= narrows them; ?all=true gives every lane kept). GET /v1/lanes/{id} — one lane.
POST /v1/lanes — { "cards": [483, 481], "reviewer": "R1", "rounds": 10, "waitMinutes": 360, "name": "lanes and loops" } starts a lane, or updates the builder's running one. The calling agent is its builder; the Assistant and scripts add project and builder (a name or id). A reviewer Hive doesn't know leaves the lane without one (the reply's note says so) rather than refusing. Rounds and the wait left out are the project's card-loop settings (Project Settings → Card loops), else the workspace's (Settings → Workspace), else 10 and 360 minutes. Replies { lane, did: "started" | "updated", replaced }: replaced is the builder's earlier lane, if starting this one ended it. When the Assistant starts one and no card watch of its own covers the lane's last card, note says to watch it (#402).
POST /v1/lanes/join — { "builder": "B1", "cards": [483, 481] }: the calling agent joins the builder's running lane as its reviewer (a script adds project and reviewer); with cards, only a running lane that shares one (a builder's earlier lane, its cards passed and waiting for the merge, is another loop). With no such lane yet, one is started from cards as the builder's start would, and the builder's own start then updates it. Replies { lane, did: "joined" }.
POST /v1/lanes/{id}/end — { "mergeCommit": "a1b2c3d" } ends it, or { "abandoned": true }. Only its builder or reviewer ends it among agents.
The event stream sends lanes-changed ({ workspacePath }: read the lanes again) when a lane changes, or one of its cards does.
Skills and MCP
GET /v1/skills[?project=name] — the workspace's Hive skills, each provider's user (level: "machine") and plugin skills, plus, if a project is given, the project's local skills, and then only the Hive skills its agents get (not those whose audience is assistant): { name, description, level, provider?, plugin?, audience?, bundled?, updateAvailable? }, without folder paths. audience (Hive skills) is who gets it: agents, assistant or all (its SKILL.md's metadata.audience; agents without one). problem (Hive skills) says why nobody gets it: its header can't be read, or its audience isn't agents, assistant or all. bundled is same or changed for Hive skills that ship with Hive, and updateAvailable marks a changed copy made from a version this Hive knows to be older than its own. A project agent may list only its own project's skills (403 for another's).
GET /v1/skills/{name}[?file=path] — one of the workspace's Hive skills: { name, audience, file, content, files }, its SKILL.md and the list of the files in its folder; with file (relative to the skill's folder, / between parts), that file instead ({ name, audience, file, content }). The whole path is checked before anything is read: an absolute path, .., an empty part or a link leading outside the skill's folder is 400, a file it doesn't have 404, a file over 512 KB 413 (also when it grew past that while being read: no more than 512 KB is ever read). files is in path order, without Hive's .hive-copy marker and without following links; it lists at most 200 files, looking at no more than 2000 entries or 8 folders deep, and has filesTruncated: true when there may be more (each can still be read with file). Reading a skill grants nothing: what a caller may do is still its token's.
GET /v1/mcp — MCP servers deployed to the workspace, each available to every project: { name, description, globallyEnabled, error }. globallyEnabled means on for all projects (a project can still turn it off); a server is otherwise on only in the projects that turned it on. Which servers a project has on is in GET /v1/projects/{name} (mcp).
Performance metrics
GET /v1/metrics[?scope=workspace|project&project=name&from=…&to=…&trend=1&role=agent|assistant|api|viewer&provider=id&own=1] — the workspace's performance metrics (Hive's API, its tools, its guidance and skill service) for a range (ISO times; default the last 24 hours): { scope, workspacePath, from, to, recording, projects, workspace?, skills?, app?, providers, notMeasured, dropped }. projects has a part per project (api, mcp, guidance, catalog series); with scope=workspace (the default) there is also workspace (work of no project: the Assistant, scripts), skills (the skill service) and app (process-wide: refused requests, event streams, requests in flight). The parts add up to the totals once each. A project agent may only ask for its own project (scope=project&project=<its project>; anything else is 403). Units: UTF-8 bytes, characters (UTF-16 code units), milliseconds (count, totalMs, maxMs, and a histogram over bounds of 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000 and 10000 ms, plus one for longer). providers is the providers' own reported usage for sessions active in the range (each session's total; unknown counts sessions with none reported). Hive estimates no tokens. notMeasured lists what isn't measured. Labels are only Hive's route templates, its own tool names and provider ids (anything else is (other)). project is matched case-insensitively; a project scope's dropped counts only that project's dropped measurements, and lossesUnattributed: true says some losses in the range couldn't be attributed to any project (so this one's may be more; no count is given); a workspace scope has droppedUntracked, the count of those; guidance[].skillsUnmeasured counts delivered skills whose copy couldn't be measured (not in the sizes). role, provider and own=1 (a workspace scope: the workspace's own work only) narrow every part, the trend and providers alike; filters echoes them with notFiltered, what they couldn't narrow (requests and tool calls aren't per provider). coverage (rangeMs, observedMs, observedSince, stretches, and evictedThrough when history in the range was removed to keep the metrics file under its size limit) says when Hive was recording this workspace: outside it nothing was seen (no data, not zero). guidance[] has guidanceBytes (core contract), customBytes (the project's additions), roleBytes and personaBytes (the Assistant's), each with its chars. providers[] rows are per provider and role (agent, assistant), with running, contextSessions/contextAvgTokens/contextMaxTokens/contextWindow (a snapshot, not a sum), and sessions with no usage counted in both unknown and costUnknown; providersNote says why it is empty (scripts have no sessions); providersUnreadable (of providersHosts) counts session histories that couldn't be read, so providers is partial. With trend=1 the report also has trend ({ start, observedMs, requests, failed, cancelled, requestBytes, responseBytes, toolCalls, toolChars, launches, guidanceBytes } for every hour of a range up to 2 days, else every day, at most 200) and trendStep (hour or day), for the scope's parts. If the workspace closes or switches while the report is read, it is refused (400).
POST /v1/metrics/mcp — for the hive MCP bridge only (an agent's or the Assistant's own token; 403 otherwise): { calls: [{ tool, mode: "compact"|"detail", ok, chars, bytes, ms }], catalog?: { tools, toolsBytes } }, at most 50 calls. Returns { accepted, refused }; a malformed entry is refused, not stored. Not itself counted as API traffic.
Progress
Long runs (test suites, builds) report their progress for the Progress panel, which shows each one with its agent, a bar and the time left, and Hive's taskbar button.
GET /v1/progress — the Progress panel's runs, newest first, as the panel lists them (Recent from before a restart included), in their public form: { workspacePath, enabled, runs }. Each run has its id, who reported it (source: agent, assistant or api; projectPath, agentId, provider, and agentName: the agent's, Hive Assistant or Script), title, titleFromCommand, total, step, estimateMs, expectedMs, state (running, stale, passed, failed, stopped), staleReason, stopReason, waiting (whether it waits, true/false) with waitingSince, exitCode, dismissed (listed under Recent), and startedAt, updatedAt and finishedAt (ms). Left out, for every token: the command line (its arguments can hold a token or a prompt), the step's name, what it waits for, its summary and its log's path: a reporter's own text, which can quote output. A title leaves Hive only when it was given on purpose (hive-progress --title, the title a reporter or script gives, Hive's own test runners'). A run started with no title of its own (hive-progress without --title) is titled with its command line inside Hive; here its title is "a command" and titleFromCommand is true, and no part of the command line is given. The run says so itself (titleFromCommand, from hive-progress); one that doesn't (an older hive-progress, a run kept from before) whose title begins as its command line does is taken for one too. A project agent's token gets its own project's runs only. With the panel turned off, enabled is false and runs is empty. progress-changed on the event stream sends the same list as it changes. A run belongs to whoever starts it, known by the caller's token: a project agent's token makes it that agent's, the Assistant's its own, the workspace token a script's ("Script"). Only that agent may update or finish it; the Assistant only its own; a script any run in its workspace. Another caller's run answers 404, as an unknown one does. Runs are kept in memory, per workspace: after Hive restarts, or once the workspace closes (or its window opens another), an old id is 404, which a reporter should ignore.
POST /v1/progress — start a run. Returns { "id": "…" }.
json{ "title": "e2e: 12 suites", "total": 12, "step": 0, "stepName": "about", "estimateMs": 360000, "command": "npm run e2e" }
title(required, up to 120 characters): what is running.total(1–100000) andstep(0–total):stepcounts finished steps. Withouttotal, the panel shows a moving bar and the time.stepName(up to 120): the step running now.estimateMs(0–7 days): the estimated time left, counted from this report. The panel shows "about N min left"; past it, "finishing" for a minute and then "longer than expected", never "0 s left".command(up to 200): shown on hover.titleFromCommand(true/false): the title is the command line, ashive-progresssends when it has no--title. Such a title never leaves Hive:GET /v1/progressand the event stream call the run"a command"(below), since a command's arguments can hold a token or a prompt.waiting(up to 120): what the run waits for before it can go on, such as"waiting for a test slot: held by B4". While it waits the panel shows that and how long it has waited, never a time left, with a still, hatched bar; it doesn't go stale meanwhile. The estimate is held: the time that was left is given back from the moment it stops waiting.pid: the reporting process (Hive's reporters send it). If that process is gone before the run finishes (killed with its command), Hive ends the run as ended without a report instead of leaving it to go stale.
PATCH /v1/progress/{id} — { "total"?, "step"?, "stepName"?, "estimateMs"?, "waiting"? }: an update. waiting says what it waits for now ("": it waits no more). Returns { "ok": true }. total (1–100000) may be set once, on a run started without one (a command that learns how many steps it has after it started): from then on the panel shows its steps, and a step already past it is lowered to it; on a run that has a total, total is 409. Without estimateMs the deadline stays where it was (the time left keeps counting down); only a new estimateMs moves it. A few updates a second are shown; more are merged, not refused. An update after a run went stale (no report for longer than expected) makes it running again. A finished run is 409.
POST /v1/progress/{id}/finish — { "ok": true|false, "stopped"?, "summary"?, "exitCode"?, "logPath"? }: the run passed or failed; with "stopped": true it was interrupted (Ctrl+C) and shows as stopped, neither passed nor failed, whatever ok says. Returns { "ok": true }. summary (up to 500 characters) shows under a failed run; exitCode (a whole number) is the command's exit code, and logPath (up to 400) a log or run record it wrote: both show in the run's details in the panel.
At most 5 runs can be open at once per agent (per Assistant, per workspace's scripts), and a workspace keeps at most 30 runs whoever reported them: ended ones make room, oldest first, and when all 30 are open another start is 429. A request that is refused (400) changes nothing. A run with no report for longer than expected goes stale: with steps and an estimate, a step's share of the time left plus 2 minutes; with only an estimate, the estimate plus 2 minutes; with neither, 10 minutes. An agent's run also goes stale when the agent stops. Dismissing a stale run in the panel ends it: it leaves the agent's status and the taskbar, and further updates are 409.
With Settings → General → Progress panel off, every call is accepted and ignored: a start returns { "id": "…", "ignored": true }, and updates and finishes { "ok": true, "ignored": true }. A report that was already on its way when the setting was turned off (or off and on again) is ignored the same way, and keeps nothing.
An agent's open run shows in its status (GET /v1/projects/{name} agents, and its activity) as progress: { title, step?, total?, stepName?, waiting?, etaMs?, stale? }, etaMs being the time left now (none while it waits).
Reporting a command: hive-progress
Every session Hive starts has hive-progress on its PATH (cmd, PowerShell and Git Bash), so a command needs no code to report:
bashhive-progress [--title "e2e"] -- <command> [args...]
It runs the command with its output passed through unchanged and ends with its exit code, and reports it as the agent's run: the title (the command line when none is given; Hive's session guidance asks agents for a few words saying what and why, such as --title "e2e: full set before merging" or --title "unit tests", so the Progress panel reads as a description and its details show the exact command beside it), the finish (passed when the exit code is 0, otherwise failed with exit code N), and an estimate from how long the same command in the same folder took the last few times it passed (kept in Hive's data, never in the project). With no Hive variables, Hive unreachable or a refused call, it just runs the command, and it never fails because of Hive. HIVE_PROGRESS=0 turns reporting off.
Steps. A command (or any project's test runner) that prints lines like this gets real steps:
##hive-progress step=4 total=12 name=carddialog
step is the step starting now, from 1; total how many there are; name the rest of the line. A line ##hive-progress log=<path> names a log or run record the command wrote (the rest of the line); hive-progress sends it with the finish, with the command's exit code (npm run e2e names its run record, or its logs folder). A line ##hive-progress wait=<what> says the command waits (for a queue or a lock: Hive's test runners print it while they wait for a test slot), and ##hive-progress wait= that it waits no more: the run shows as waiting, and the wait isn't counted in the command's usual time. A command that ends by Ctrl+C (Windows' STATUS_CONTROL_C_EXIT, or a shell's 130) is reported as stopped, not failed, and so is one whose hive-progress gets Ctrl+C itself (however the command then exits: its exit code is passed on, and the run isn't counted in its usual time; if it doesn't end within 5 s, or on a second Ctrl+C, it is ended with everything it started); one whose hive-progress is killed (an agent stopping a preview server ends the whole process tree) shows as ended without a report. The lines are taken out of the output. hive-progress starts its run at the first step line that gives a total, or after 2 seconds, so a total printed early shows as steps from the start. A run started without one takes the first total printed later (sent once with PATCH); a different total after that is ignored, the steps aren't.
Hive's own test runners report the same way (npm run e2e, a step per suite; npm test, a step per file) when they run in an agent's session.
Inside hive-progress. The command it runs has HIVE_PROGRESS_WRAPPED=1, and the run is the wrapper's: a reporter in it should print step lines rather than start a run of its own, which would be a second row. Another hive-progress inside one just runs its command; Hive's own runners print step lines.
Agents use hive-progress unasked for commands likely to take over 30 seconds, unless Settings → General → Agents show long commands in the Progress panel, or the Progress panel itself, is off: Hive's session guidance tells them which.
Developer console
What each CLI sends Hive (its hooks, status lines, transcript records, the terminal markers Hive reacts to, the lines Hive types into it, its launch and exit, its own API calls), each next to what Hive made of it (the status it set, the details it read), and Hive's own events in a workspace (Agent API calls, merge slots, watches waking agents, Progress runs, refused hooks). Read-only. These routes answer only while Settings → Agent API → Let agents read the developer console is on (otherwise 403).
- Who reads what: a project agent (its own token) reads its own session only; the Hive Assistant reads its workspace's agents, its own session and Hive's events there; the workspace token and viewers read any open workspace's. A source the caller may not read, and one an agent names that doesn't exist, is
404with the same message. - Naming a source:
agent(a name or id) andproject(an agent's own project by default), orsource=assistantorsource=hive(the request's workspace). A project agent naming none reads its own session. - Capture: only while the user has the developer console open on a source; reading starts nothing, and a read of a source no open console captures says so (
source.capturing: false, no events). Each source keeps at most its newest 500 events or 1 MB, in memory only, and nothing once the console stops watching it. - Redacted: strings are kept only at the paths listed for each kind of payload (a hook's envelope: its name, session id, tool name, mode, folder; a tool input's file path; a status line's model; a transcript record's type and model) and only when they look like an identifier, a path or a time; every other string, one word or many, becomes
<text: N chars>, so a tool's arguments, an unknown record and an unknown field's id or name stay hidden. A secret or sign-in field (a token, authorization, a login, a device code) is<redacted>with everything under it, and a prompt's or message's subtree keeps none of its strings.
GET /v1/console/sources[?offset=n&limit=n] — the sources the caller can read, 50 a page (limit up to 100): { total, offset, sources: [{ label, kind: "session"|"assistant"|"hive", select, provider?, capturing, events }] }. label is for display (cut and masked); select names the source exactly as a read takes it: { source: "hive" }, { source: "assistant" }, or { agent: <its id>, project: <its project's name> }.
GET /v1/console/events?agent=…&project=…|source=…[&since=n&limit=n&kinds=hook,statusline,…|&seq=n,n] — what an open console has captured of the source: { source, events, newest, older?|newer? }. events are rows { seq, at, kind, name, summary } (truncated when a payload was too large and kept as its outline): the newest 30 the first time (limit up to 100), oldest first after since (pass the reply's newest to carry on; newer counts those still to come). kinds narrows them: hook, statusline, transcript, terminal, input, launch, exit, api, hook-server, watch, merge-slot, progress. With seq (up to 3), the events in full instead, each with its raw payload and made (what Hive made of it), plus missing for those no longer kept. seq numbers rise across every source. The console's own reads aren't events.
Notifications
POST /v1/notify — show a notification in Hive.
json{ "title": "Build finished", "message": "All 212 tests passed", "level": "success" }
level is info (default), success, warning or error.
Events (server-sent events)
GET /v1/events — a stream of events as they happen:
event: session-status
data: {"type":"session-status","state":{"projectPath":"D:\\work\\api","sessionId":"6f1c…","status":"finished",…}}
Event types:
| Event | Data | Means |
|---|---|---|
session-status |
{ state }: an agent's live state in its public form (below) |
An agent's status changed |
session-exit |
{ projectPath, agentId, sessionId, exitCode, failedToStart? } (true when its CLI exited before the session started; not why) |
An agent's session ended |
workspace-changed |
{ workspace } (its path) |
Projects or agents changed (added, removed, renamed): read / again |
notes-changed, skills-changed |
(none) | Read them again |
tasks-changed |
{ workspacePath } |
Read the board again |
lanes-changed |
{ workspacePath } |
Read the lanes again |
progress-changed |
{ workspacePath, runs }: every run, in the public form GET / gives |
A progress run started, moved on or ended |
merge-slots-changed |
{ workspacePath, slots }: the slots held or waited for, each { project (its folder), branch, holder, waiting } as GET / gives it |
A merge slot was taken, released or waited for |
notice |
{ notice: { id, kind, title, projectPath, agentId? } }: kind is finished, waiting or notice; projectPath is null for an app-wide one; title is in English, whatever language Hive shows |
Hive told the user something (a banner or a Windows notification; not when notifications are off). Its body (an agent's last reply, its question) isn't sent |
notice-resolved |
{ projectPath, agentId } |
That agent no longer waits for the user |
assistant-activity |
{ workspacePath, action: { id, at, text, ok, error?, revertOf?, kind, project?, agent?, toAgent?, card?, cards?, lane?, column?, settingId? } } |
The Hive Assistant did something, or was refused (its panel's list). text is the line for people, without what it quotes (the prompt or message it gave an agent, a setting's old and new values), and may be reworded or translated: a viewer reads kind and its targets instead (Assistant activity, since api.version 5) |
agent-added |
{ projectPath, agentId } |
The Assistant (or a card it started) added an agent; it also comes as workspace-changed |
Nothing private is sent, to any token. Every payload is built from a list of the fields it may carry, so there is no prompt, message, question or reply text, no terminal or CLI output, no command line, no notice body and no setting's value. A session-status event's state has: provider, projectPath, agentId, agentName, cwd, sessionId, runId, sessionName, status, statusSince, startedAt, unseen, mergeSlot and watch (Hive's own words: "Waiting for the merge slot (B4 is merging #305)", "Waiting for #12 → Review", with the watch's cards, agents, lanes, column, changes, since and limitAt), notWatching, question: { since } (it asked something without stopping; not what), reviewing: true (an action under the CLI's automatic review; not which), signIn: { since }, untakenLine: { since } (a line Hive typed that it hasn't taken), inbox: { unread: [{ id, from, at, state }], overdue } (its deliveries not received yet: who from, when and their state, not what they say; and how many weren't acked in time), permissionMode, modeSwitching, planMode, modelName, modelId, effort, costUsd, costEstimated, contextWindow, backgroundTasks, settingUp and lockedFiles. Its status message (the CLI's own words) isn't sent: GET /v1/projects gives it. Events cover every open workspace, so the Hive Assistant's token is refused here (403); it follows agents with hive_wait_for_agents. The workspace token and viewer tokens get every event. A project agent's own token gets its own project's session-status, session-exit, notice, notice-resolved and agent-added only (another project's carry their session names, which can name their cards), progress-changed and merge-slots-changed for its own workspace with its project's runs and slots only, and no assistant-activity; the other events carry no more than a path and come to it as to anyone. A client that stops reading (more than 1 MB of events waiting) is disconnected.
bashcurl -N -H "Authorization: Bearer $HIVE_API_TOKEN" "$HIVE_API_URL/v1/events"
Assistant activity
Each assistant-activity action says what it was with fields, from api.version 5 (#763), so a viewer (Apiary walking the Assistant to an agent's desk or the board) never reads text. kind is one of the set below; a refusal (ok: false) has the kind it tried, and its error is Hive's own short reason (Not allowed, Not allowed by Settings → Assistant → Control, Not possible now, The request was invalid, Not found, Over the limit of changes for one message, Refused, Dropped as stale, Failed), never the error's own text, which can quote what the Assistant sent; nor does text carry one (an agent added whose start failed is "Added B2 to hive; it didn't start"). The Assistant's panel shows the whole message. Its targets come wherever Hive knows them, on a refusal too:
project: the project, by name;agent:{ id, name }, the agent it acted on: the one a card is started on, given to or is with after a change (also in a batch), a hand-over's from, a lane's builder, the reviewer joining a lane. An agent that doesn't exist yet (one being added) isn't named until it does;toAgent:{ id, name }, a hand-over's to;card: a card's number;cards: a lane's, a reorder's or a batch's cards;lane: a lane's id;column: where the card is after a move, or the column put in order (hold,todo,doing,review,passed,done);settingId: a setting's id (ashive_list_settingslists it), never its value.
Nothing private is added: no prompt or message text and no setting's value, the same allow-list as the rest of the stream. A field Hive doesn't know when the action is refused (a new card's number) isn't there.
kind |
What the Assistant did | Example action (without id and at) |
|---|---|---|
start-task |
Started a card on an agent (hive_start_task) |
{ "kind": "start-task", "ok": true, "card": 741, "project": "amberlight", "agent": { "id": "a-3f9c01d2", "name": "AMB1" }, "column": "doing", "text": "Started #741 on AMB1 in amberlight: …" } |
prompt-agent |
Gave an agent a task (hive_prompt_agent) |
{ "kind": "prompt-agent", "ok": true, "project": "hive", "agent": { "id": "a-7b21e4aa", "name": "B1" } } |
message-agent |
Left an agent a message, maybe tied to a card (hive_message_agent); also a message of its that was refused or dropped as stale (ok: false) |
{ "kind": "message-agent", "ok": true, "project": "hive", "agent": { "id": "a-7b21e4aa", "name": "B1" }, "card": 12 } |
cancel-messages |
Cancelled the messages waiting for an agent | { "kind": "cancel-messages", "ok": true, "project": "hive", "agent": { "id": "a-7b21e4aa", "name": "B1" } } |
start-agent |
Started (or resumed) an agent | { "kind": "start-agent", "ok": true, "project": "hive", "agent": { "id": "a-7b21e4aa", "name": "B1" } } |
stop-agent |
Stopped an agent | { "kind": "stop-agent", "ok": false, "error": "Not possible now", "project": "hive", "agent": { "id": "a-7b21e4aa", "name": "B1" } } |
add-agent |
Added an agent (agent once it exists) |
{ "kind": "add-agent", "ok": true, "project": "hive", "agent": { "id": "a-0c55e1f9", "name": "B2" } } |
update-agent |
Changed an agent's settings | { "kind": "update-agent", "ok": true, "project": "hive", "agent": { "id": "a-7b21e4aa", "name": "B1" } } |
hand-over |
Handed one agent's work over to another | { "kind": "hand-over", "ok": true, "project": "hive", "agent": { "id": "a-7b21e4aa", "name": "B1" }, "toAgent": { "id": "a-0c55e1f9", "name": "B2" } } |
create-task |
Added a card | { "kind": "create-task", "ok": true, "card": 763, "column": "todo", "project": "hive" } |
update-task |
Changed, moved or commented on one card (hive_update_task) |
{ "kind": "update-task", "ok": true, "card": 12, "column": "review", "project": "hive", "agent": { "id": "a-7b21e4aa", "name": "B1" } } |
update-tasks |
A batch (hive_update_tasks): one action per card, each with its card and where it is |
{ "kind": "update-tasks", "ok": true, "card": 14, "column": "done", "project": "hive", "agent": { "id": "a-7b21e4aa", "name": "B1" } } |
reorder-tasks |
Put cards in order in a column | { "kind": "reorder-tasks", "ok": true, "column": "todo", "cards": [14, 12] } |
comment-task |
Commented on a card | { "kind": "comment-task", "ok": true, "card": 12 } |
lane-start |
Started (or changed) a lane | { "kind": "lane-start", "ok": true, "lane": "lane-0f89a0", "project": "hive", "cards": [713, 763], "agent": { "id": "a-745674db", "name": "HB3" } } |
lane-join |
Had a reviewer join a lane (agent: the reviewer) |
{ "kind": "lane-join", "ok": true, "lane": "lane-0f89a0", "project": "hive", "cards": [713, 763], "agent": { "id": "a-1d2e3f40", "name": "HR3" } } |
lane-end |
Ended a lane (merged or abandoned) | { "kind": "lane-end", "ok": true, "lane": "lane-0f89a0", "project": "hive", "cards": [713, 763], "agent": { "id": "a-745674db", "name": "HB3" } } |
create-project |
Created a project | { "kind": "create-project", "ok": true, "project": "apiary" } |
activate-project |
Turned a project on | { "kind": "activate-project", "ok": true, "project": "apiary" } |
deactivate-project |
Was refused turning a project off (only refusals are listed) | { "kind": "deactivate-project", "ok": false, "error": "Not allowed by Settings → Assistant → Control", "project": "apiary" } |
setting |
Changed one of Hive's settings | { "kind": "setting", "ok": true, "settingId": "sessions.confirmStop" } |
revert-setting |
The user reverted a setting the Assistant changed (revertOf: that action's id) |
{ "kind": "revert-setting", "ok": true, "settingId": "sessions.confirmStop", "revertOf": "…" } |
The built-in hive MCP server
When Provide Hive tools to sessions is on (the default), Hive adds an MCP server named hive to every session it starts, whatever its provider. It wraps the API above. Its replies are kept short, since everything a tool returns goes into the agent's context: a change confirms what changed and what the agent can't already know (a new card's number, where a card is now), a listing gives one line per item, and full detail comes from the matching read tool or on request (hive_list_tasks with details: true, hive_read_task with history: true, hive_session_usage with days: true). Structured reads such as hive_project_status are compact JSON; notes and handovers are read as their text.
| Tool | Endpoint |
|---|---|
hive_list_projects |
GET /, a line per project |
hive_project_status |
GET / |
hive_session_usage |
GET / (days=false unless days: true) |
hive_list_shared_notes |
GET /, a path per line |
hive_read_shared_note |
GET / |
hive_write_shared_note |
PUT / |
hive_read_latest_handover |
GET /, then GET / for the project's newest handover (see below) |
hive_create_handover |
POST / |
hive_notify |
POST / |
hive_inbox |
POST / (ack: [ids] to ack), a line per item (agents and the Assistant) |
hive_list_skills |
GET /, a line per skill (the session's project's, for an agent) |
hive_list_tasks |
GET /, a line per card by column (at most 200 a reply, offset for the next; details: true for whole cards) |
hive_read_task |
GET / (history: true for its history, comments: n for its newest n comments; latestComment: true: /; summary: true: ?summary=true) |
hive_create_task |
POST / (the session's project by default), short reply |
hive_update_task |
PATCH / (with comment), short reply |
hive_reorder_tasks |
POST /, short reply |
hive_update_tasks |
POST /, short reply |
hive_wait_for_tasks |
POST / (wake: true for a card watch, cancel: true to end it), a line per change |
hive_wait_for_agents |
POST / (50 seconds by default, it calls again to keep waiting; wake: true for an agent watch, a line; cancel: true to end it) |
hive_merge_slot |
GET /, POST …/ or …/ (the session's project), a line (project agents only) |
hive_lane |
GET /, POST / (start), POST / or POST /, a line per lane (an agent: its own project's, as the builder or reviewer) |
hive_console |
GET / or GET /, a line per event (its own session; offered only while Settings → Agent API → Let agents read the developer console is on) |
Tools default to the session's own project, so an agent can simply say "create a handover". The board tools send the agent's id and project, so a card's history and comments name the agent.
The Hive Assistant's hive server always runs (even with this setting or the Agent API off) and has more tools, as far as its control level allows:
| Tool | Endpoint | Control |
|---|---|---|
hive_list_providers |
GET / |
any |
hive_agent_activity |
GET / (detail: true: ?detail=true) |
any |
hive_activate_project |
POST / |
Control agents |
hive_add_agent |
POST / |
Control agents |
hive_update_agent |
PATCH / |
Control agents |
hive_start_agent |
POST / |
Control agents |
hive_stop_agent |
POST / |
Control agents |
hive_prompt_agent |
POST / |
Control agents |
hive_message_agent |
POST / |
Control agents |
hive_hand_over |
POST / |
Control agents |
hive_start_task |
POST / |
Control agents |
hive_create_project |
POST / |
Control agents and create projects |
hive_list_settings |
GET /, a line per setting (what each does only with query) |
any |
hive_read_setting |
GET /, as text |
any |
hive_update_setting |
PATCH /, old → new |
Change settings on (any level) |
It also has the board tools above; Claude Code runs hive_create_task, hive_update_task, hive_update_tasks, hive_reorder_tasks and hive_lane without asking from Control agents up. Claude Code runs these without asking (they are Hive's own, and limited by the control level); Codex gets a 15-minute tool timeout for them, since waiting and asking the user can take minutes.
A handover belongs to a project when its file name is handovers/<date>-<project>-<title>.md, as hive_create_handover writes it. When another project's name begins the same way (hive and hive-website), the **Project:** line at the top of the handover decides. Hive records whose each handover is by the project's stable id as it writes it (older ones once, from what they say), so a project renamed in the workspace keeps its handovers, and a project that later takes its old name never gets them.
The server's instructions (for Codex, which doesn't show MCP server instructions to the model, Hive passes the same text as developer instructions) are Hive's short session contract: that handovers, shared notes, other projects and the board are Hive's and are reached with these tools rather than by searching the file system, which of Hive's skills covers which workflow (work-on-card, review-agent-work, and coordinate-agents for the Assistant), and the board's boundaries in a line, which hold even when a skill is missing. When the project has a handover, the instructions also name the latest one, so a new session knows it is there from the start. The tools' descriptions say what each takes, changes and answers; how to carry out a workflow is the skills'. GET /v1/status gives the revision of this guidance, and GET /v1/projects/{name} each running agent's launched revisions (its guidance and skills at launch).
The server is a small Node script bundled with Hive and run by Hive's own executable, so nothing else needs to be installed.
Security notes
- The API binds to
127.0.0.1only; it is not reachable from other machines. - Any local program that can read the token can use the API. Regenerate the token in Settings if it leaks.
- Give an app that only shows Hive a viewer token, not the workspace token: it can read everything but change nothing, and revoking it cuts that app off alone. The discovery file holds no token.
- Session input is off by default because it lets one agent drive another project's session.
- The Hive Assistant's token is separate, replaced each time it starts, and limited to its workspace and to Settings → Assistant → Control.
- Shared-note paths are confined to
.hive/shared.