--- title: Socket API description: Control a running Herdr server from scripts, tools, and coding agents. --- Herdr exposes a local socket API for scripts and agents that need to inspect or control a running session. Most automation should start with the CLI wrappers. Use the raw socket API only when you need direct request/response control or long-lived event subscriptions. ## Choose an integration layer | Layer | Use it for | | --- | --- | | Agent skill | Teaching a coding agent how to use Herdr from inside a pane. | | CLI wrappers | Shell scripts, simple orchestration, and human debugging. | | Raw socket API | Custom tools, protocol clients, and event subscribers. | The layers share the same control surface. ## What you can control The socket API can: - create, list, focus, rename, and close workspaces - create, list, focus, rename, and close tabs - list, inspect, split, swap, focus, resize, rename, read, close, and send input to panes - list, inspect, read, send to, rename, focus, start, and attach agents through CLI helpers - report custom agent state from hooks and plugins - subscribe to events and wait for output or state changes - install and uninstall built-in integrations - stop the server and reload config ## CLI examples Create a workspace: ```bash herdr workspace create --cwd ~/project --label api ``` Create a tab: ```bash herdr tab create --label logs ``` Split a pane and run a command: ```bash herdr pane split 1-1 --direction right herdr pane run 1-2 "npm test" ``` Inspect and rearrange panes: ```bash herdr pane layout --current herdr pane neighbor --direction right --current herdr pane resize --direction right --amount 0.1 --current herdr pane swap --direction right --current herdr pane zoom --on --current herdr pane split 1-1 --direction right --ratio 0.333 ``` Wait for an agent: ```bash herdr wait agent-status 1-1 --status done ``` Read pane output: ```bash herdr pane read 1-2 --source recent --lines 50 ``` ## Raw methods Raw socket method names use dot notation: | Area | Methods | | --- | --- | | Server | `ping`, `server.stop`, `server.reload_config`, `server.agent_manifests`, `server.reload_agent_manifests` | | Notification | `notification.show` | | Workspace | `workspace.create`, `workspace.list`, `workspace.get`, `workspace.focus`, `workspace.rename`, `workspace.close` | | Worktree | `worktree.list`, `worktree.create`, `worktree.open`, `worktree.remove` | | Tab | `tab.create`, `tab.list`, `tab.get`, `tab.focus`, `tab.rename`, `tab.close` | | Pane | `pane.split`, `pane.swap`, `pane.zoom`, `pane.layout`, `pane.neighbor`, `pane.edges`, `pane.focus_direction`, `pane.resize`, `pane.list`, `pane.get`, `pane.rename`, `pane.send_text`, `pane.send_keys`, `pane.send_input`, `pane.read`, `pane.report_agent`, `pane.report_agent_session`, `pane.report_metadata`, `pane.clear_agent_authority`, `pane.release_agent`, `pane.close`, `pane.wait_for_output` | | Agent | `agent.list`, `agent.get`, `agent.read`, `agent.explain`, `agent.send`, `agent.rename`, `agent.focus`, `agent.start` | | Events | `events.subscribe`, `events.wait` | | Integrations | `integration.install`, `integration.uninstall` | Some CLI commands are conveniences around these methods. For example, `herdr agent wait` resolves an agent target and then subscribes to pane agent state events. Pane control methods use public pane ids such as `1-1`. Omit `pane_id` to use the server's active focused pane. ```json {"id":"req_layout","method":"pane.layout","params":{"pane_id":"1-1"}} {"id":"req_neighbor","method":"pane.neighbor","params":{"pane_id":"1-1","direction":"right"}} {"id":"req_edges","method":"pane.edges","params":{"pane_id":"1-1"}} {"id":"req_focus","method":"pane.focus_direction","params":{"direction":"right"}} {"id":"req_resize","method":"pane.resize","params":{"pane_id":"1-1","direction":"right","amount":0.1}} {"id":"req_zoom","method":"pane.zoom","params":{"pane_id":"1-1","mode":"toggle"}} {"id":"req_split","method":"pane.split","params":{"direction":"right","ratio":0.333}} ``` `pane.layout` returns the tab layout snapshot with `workspace_id`, `tab_id`, `zoomed`, outer `area`, `focused_pane_id`, pane rects, and split rects/ratios. `pane.neighbor` and `pane.edges` include that same layout snapshot so clients can make the next decision without private layout state. `pane.swap` supports directional and explicit forms: ```json {"id":"req_swap_dir","method":"pane.swap","params":{"pane_id":"1-1","direction":"right"}} {"id":"req_swap_explicit","method":"pane.swap","params":{"source_pane_id":"1-1","target_pane_id":"1-2"}} ``` Swap is same-tab only. It preserves split shape, split ratios, pane ids, and running processes. The response is `type: "pane_swap"` with `changed`, optional `reason`, `source_pane_id`, optional `target_pane_id`, `focused_pane_id`, and `layout`. Reason values are `no_neighbor`, `same_pane`, `not_found`, and `cross_tab`. When a tab is zoomed, swap keeps zoom active and mutates the hidden full-tab layout. `pane.zoom` toggles, enables, or disables zoom for the target pane's tab: ```json {"id":"req_zoom_toggle","method":"pane.zoom","params":{"pane_id":"1-1"}} {"id":"req_zoom_on","method":"pane.zoom","params":{"pane_id":"1-1","mode":"on"}} {"id":"req_zoom_off","method":"pane.zoom","params":{"pane_id":"1-1","mode":"off"}} ``` Omitting `pane_id` targets the server's active focused pane. The response is `type: "pane_zoom"` with `changed`, `zoom_changed`, `focus_changed`, optional `reason`, `pane_id`, `focused_pane_id`, `zoomed`, and `layout`. `changed` is true when either zoom state or focus changed. Reason values are `single_pane`, `already_zoomed`, and `already_unzoomed`. The CLI wrapper for `notification.show` is: ```bash herdr notification show "build failed" --body "api workspace" --position top-left --sound request ``` Show a user notification through the configured toast delivery: ```json {"id":"req_notify","method":"notification.show","params":{"title":"build failed","body":"api workspace","position":"top-left","sound":"request"}} ``` `title` is required and must contain visible text after control characters and repeated whitespace are removed. `body` is optional. Herdr collapses newlines, tabs, carriage returns, and repeated whitespace into spaces, then trims notification text to 80 characters for `title` and 240 characters for `body`. An empty sanitized `title` returns `invalid_params`. `position` is optional and applies only when `ui.toast.delivery = "herdr"`; desktop positions are relative to the full Herdr frame, and omitted positions use `ui.toast.herdr.position`. Terminal, system, and off delivery ignore `position`. `sound` is optional and can be `none`, `done`, or `request`; it defaults to `none` and plays only when the notification is shown. The response reports whether anything was shown: ```json {"id":"req_notify","result":{"type":"notification_show","shown":true,"reason":"shown"}} ``` Possible reasons are `shown`, `disabled`, `rate_limited`, `no_foreground_client`, and `busy`. `disabled` means `ui.toast.delivery = "off"`. `busy` means an existing in-app toast was not replaced. Terminal and system delivery are best-effort through the current foreground attached Herdr client. Worktree methods manage Git checkouts as Herdr workspaces. `worktree.create` creates a checkout and returns the new `workspace`, `tab`, `root_pane`, and `worktree` records. `worktree.open` opens an existing checkout or returns the already-open workspace. `worktree.remove` runs `git worktree remove` against a linked child workspace and never deletes the branch. Create a worktree from a source workspace: ```json {"id":"req_1","method":"worktree.create","params":{"workspace_id":"1","branch":"worktree/api","focus":false}} ``` Open an existing checkout: ```json {"id":"req_2","method":"worktree.open","params":{"workspace_id":"1","branch":"worktree/api","focus":true}} ``` Remove a linked checkout: ```json {"id":"req_3","method":"worktree.remove","params":{"workspace_id":"2","force":false}} ``` Use at most one of `workspace_id` or `cwd` for `worktree.list`, `worktree.create`, and `worktree.open`; omit both to use the active workspace. Use exactly one of `path` or `branch` for `worktree.open`. Raw socket `cwd` and `path` values must be absolute; the CLI expands relative `--cwd` and `--path` values before sending requests. Workspace responses include optional `worktree` provenance when a workspace belongs to a Herdr worktree group. Worktree commands can emit `workspace.updated` when an existing workspace gains or changes worktree provenance. ## Socket transport Herdr uses newline-delimited JSON over a Unix domain socket. Send one request per line: ```json {"id":"req_1","method":"ping","params":{}} ``` A successful response includes the same `id`: ```json {"id":"req_1","result":{"type":"pong"}} ``` Event subscriptions keep the connection open after the initial response. ## Socket paths The default socket lives under your Herdr config directory. Named sessions have separate sockets: ```text ~/.config/herdr/herdr.sock ~/.config/herdr/sessions//herdr.sock ``` Resolution order: 1. explicit CLI `--session ` 2. `HERDR_SOCKET_PATH` 3. `HERDR_SESSION=` 4. default session socket Use `HERDR_SOCKET_PATH` only for low-level overrides. ## Agent state reporting Integrations report agent state with `pane.report_agent`. ```json { "id": "req_1", "method": "pane.report_agent", "params": { "pane_id": "1-1", "source": "custom:docs", "agent": "docs-bot", "state": "working", "message": "building docs", "custom_status": "indexing" } } ``` `state` is semantic. It affects waits, notifications, and rollups. `custom_status` is visual. It can show a short label like `indexing` without changing semantic behavior. Session-only official integrations report native session references with `pane.report_agent_session`. State-reporting integrations can still include native session references in `pane.report_agent`. State-independent session reports do not affect waits, notifications, or rollups. ```json { "id": "req_2", "method": "pane.report_agent_session", "params": { "pane_id": "1-1", "source": "herdr:codex", "agent": "codex", "agent_session_id": "..." } } ``` `pane.get`, `pane.list`, `agent.get`, and `agent.list` expose a read-only `agent_session` object when Herdr has a stored native session reference: ```json { "agent_session": { "source": "herdr:codex", "agent": "codex", "kind": "id", "value": "..." } } ``` If no native session reference is stored, the field is omitted. `pane.get`, `pane.list`, `agent.get`, and `agent.list` also expose `foreground_cwd` when Herdr can resolve the cwd of the process currently controlling the pane PTY. The existing `cwd` field remains the pane/workspace cwd used for labels, follow-cwd behavior, and restored session state. Use `pane.report_metadata` when a user hook wants to customize presentation without taking over lifecycle state from a Herdr integration. ```json { "id": "req_2", "method": "pane.report_metadata", "params": { "pane_id": "1-1", "source": "user:claude-title", "agent": "claude", "title": "Refactor auth middleware", "display_agent": "Claude: auth", "custom_status": "refactor auth", "state_labels": { "working": "refactoring auth", "idle": "ready", "done": "review ready" }, "ttl_ms": 3600000 } } ``` Metadata reports are display-only. Valid metadata can override the pane title, displayed agent name, compact custom status, and visible state labels. `working`, `blocked`, `idle`, waits, notifications, and rollups still come from semantic state. Native session restore comes from stored official session references. `agent` is an optional guard for the authoritative agent label; `applies_to_source` is an optional guard for the active lifecycle authority source. Use `display_agent` to change the visible name. `state_labels` keys must be `idle`, `working`, `blocked`, `done`, or `unknown`. Use clear fields such as `clear_custom_status: true` with the same `source` to remove one presentation override. ## Event subscriptions Subscribe to events when you need a long-lived stream: ```json { "id": "sub_1", "method": "events.subscribe", "params": { "subscriptions": [ { "type": "pane.agent_status_changed", "pane_id": "1-1", "agent_status": "blocked" } ] } } ``` The first response acknowledges the subscription. Later lines are pushed events. Workspace event subscriptions include `workspace.created`, `workspace.updated`, `workspace.renamed`, `workspace.closed`, and `workspace.focused`. Use `events.wait` when you want one matching event and then a response. ## Reading panes Use `pane.read` through the CLI unless you are writing a protocol client. ```bash herdr pane read 1-1 --source visible --lines 80 herdr pane read 1-1 --source recent --lines 120 herdr pane read 1-1 --source recent-unwrapped --lines 120 herdr pane read 1-1 --source detection ``` `recent-unwrapped` is useful for logs because it ignores soft wrapping. `detection` returns the bottom-buffer snapshot used by agent screen detection. ## Waiting for state Use waits to coordinate agents and scripts. ```bash herdr wait agent-status 1-1 --status done herdr wait agent-status 1-1 --status blocked ``` Agent waits observe semantic state, not arbitrary command completion. ## Response shapes Successful responses look like this: ```json { "id": "req_1", "result": { "type": "pane_info", "pane": { "pane_id": "1-1", "terminal_id": "term_abc123", "workspace_id": "1", "tab_id": "1-1", "focused": true, "agent_status": "working", "revision": 42 } } } ``` `server.agent_manifests` returns the active agent detection manifest sources and remote update diagnostics without reloading rules: ```json { "id": "req_1", "result": { "type": "agent_manifest_status", "last_check_unix": 1781043522, "last_result": "checked", "manifests": [ { "agent": "cursor", "source": "/home/me/.config/herdr/agent-detection/cursor.toml", "source_kind": "local override", "active_version": "2026.06.10.1", "cached_remote_version": "2026.06.10.1", "local_override_shadowing_remote": true, "remote_update_result": "current" } ] } } ``` Fields such as `last_check_unix`, `last_result`, `active_version`, `cached_remote_version`, `remote_update_result`, `remote_update_error`, `remote_last_checked_unix`, and `warning` are omitted when not available. `server.reload_agent_manifests` returns `agent_manifest_reload` with the same `manifests` item shape after reloading the in-memory rule cache. `agent.explain` evaluates the target pane's detection snapshot in the running server using the server's active manifest cache: ```json { "id": "req_2", "method": "agent.explain", "params": { "target": "1-1" } } ``` The response contains the same explain object printed by `herdr agent explain --json`, including the final state, manifest source and version, matched rule, evaluated rule evidence, skip-state reason, idle fallback reason, and `screen_detection_skip_reason` when a full lifecycle hook authority makes screen rules non-authoritative. Clients need a running server that supports `agent.explain`; after upgrading Herdr, restart or live-handoff the server before relying on this method. Errors look like this: ```json { "id": "req_1", "error": { "code": "not_found", "message": "pane not found" } } ``` ## Protocol stability Herdr has a protocol version for client/server compatibility. Protocol changes are reviewed with release compatibility in mind. Check the server protocol with `ping` or `herdr status` before depending on new behavior. Handle unknown fields gracefully.