# REST API index (hal0 docs)

**URL:** <https://forum.hal0.dev/t/rest-api-index-hal0-docs/47>\
**Category:** Reference\
**Created:** [August 22, 2026, 6:50am UTC](https://forum.hal0.dev/t/rest-api-index-hal0-docs/47 "2026-08-22T06:50:11Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![system](https://forum.hal0.dev/user_avatar/forum.hal0.dev/system/32/37_2.png) [@system](https://forum.hal0.dev/u/system)\
**Post date:** [August 22, 2026, 6:50am UTC](https://forum.hal0.dev/t/rest-api-index-hal0-docs/47/1 "2026-08-22T06:50:11Z")

</div>

hal0-api serves an OpenAI-compatible `/v1/*` inference surface (see  
[OpenAI-compatible API reference](https://forum.hal0.dev/t/openai-compatible-api-reference/46)) plus an `/api/*`  
management surface that backs the dashboard and the `hal0-admin` MCP server. This page  
indexes every `/api/*` router group mounted in `src/hal0/api/ __init__.py` — for full  
request/response schemas for a given route, read the OpenAPI spec served live at  
`/api/openapi.json` (interactive docs at `/api/docs`).

> [@](#):
>
> **Caution**
> 
> Unlike pre-1.0 docs may claim, auth is **not** unconditionally removed in this tree.  
> `[security].require_auth` (overridable via `HAL0_REQUIRE_AUTH`) and the `hal0 auth`  
> CLI group (`status`, `rotate`, `require`) are live — see  
> [Config schema](https://forum.hal0.dev/t/config-schema-reference/51) (`[security]`) and  
> [CLI](https://forum.hal0.dev/t/hal0-cli-reference-memory-runner-images-profiles-mcp-auth-more/116) (`hal0 auth`).  
> Verify the current default (`require_auth` defaults to `null`, which resolves to off)  
> before stating a blanket “no auth” or “auth required” claim.

## Auth tiers

Every route resolves to one of five classes in `src/hal0/security/exposure.py`  
(`hal0.security.exposure.classify`), evaluated first-match-wins against an explicit  
rule table:

- **OPEN** — no auth ever. A deliberately tiny, enumerated allowlist (the `/v1/models`  
probe, the Prometheus scrape, the two liveness checks, the dashboard bootstrap-urls  
endpoint, the login/status/logout routes themselves, the static SPA shell — same  
HTML/JS bundle for every visitor, no server data — and the Hermes  
dashboard-plugin static asset proxy at  
`/dashboard-plugins/{plugin_name}/{file_path}`).
- **BOOTSTRAP** — open only until an admin key is configured (`/api/install/*`).
- **CLIENT** — the `/v1/*` inference/writer surface plus a short list of genuinely  
read-only introspection `GET`s.
- **BRIDGE** — the agent bridge’s own surfaces (ADR-0043): `GET /api/bridge/me` and the  
`/mcp/agents` mount, open to a bridge client key (`hal0bk_…`) or the admin key and never  
to the dev-open bypass.
- **ADMIN** — everything else, including every mutating route and every route that can  
return secrets or config. **Unclassified paths fall back to ADMIN** (deny-by-default).

These classes are fully enforced once `[security].require_auth` is on. While it’s off,  
the surface is trusted-LAN-open with one exception (since v1.3.0): with an admin key  
configured and the API bound past loopback, ADMIN-class requests from a non-loopback  
peer still need an admin credential (`_lan_admin_gate` in `src/hal0/api/auth.py`; see  
[Authentication](https://forum.hal0.dev/t/authentication-hal0-docs/43)). The table below states  
each group’s class as enforced by `exposure.py`, not as a claim about the current  
default posture.

> [@](#):
>
> **Note**
> 
> `src/hal0/api/routes/events.py`, `activity.py`, and `journal.py` each carry a  
> module docstring claiming “no auth dependency” / “no auth gate” for first-run UX  
> reasons (the dock’s events pane must render before any credential  
> exists). `exposure.py`’s own module docstring names eight routes not spelled out  
> in either explicit OPEN/CLIENT bucket list — `/api/status`, JSON `/api/metrics`,  
> `/api/features`, `/api/logs`, `/api/events`, `/api/activity`, `/api/journal`, and  
> the dashboard-plugin manifest — as an “UNSURE — please review” bucket that  
> defaults to ADMIN except where a close analogy to an existing CLIENT bucket  
> argued otherwise: `/api/status` and JSON `/api/metrics` were reasoned across to  
> CLIENT (and `/api/features` GET separately resolves CLIENT via its own exact-match  
> rule). That leaves `/api/logs`, `/api/events`, `/api/activity`, `/api/journal`,  
> and the dashboard-plugin manifest as the UNSURE items that actually landed  
> ADMIN. If `require_auth` is ever turned on, the events/activity/journal panels  
> will 401 during first-run before a credential exists — contradicting the  
> rationale their own route files state. Flagging this as a real discrepancy  
> between route-level intent and the enforced classification, not a doc error to  
> silently paper over.

## Slot tune API

`PUT /api/slots/{name}/config` accepts `{"tune":{"vision":"off"}}`.  
Only supplied keys change. A null key removes one override; `{"tune":null}`  
removes all. Slot creation applies the same type/device and thinking guards.

Slot snapshots (`GET /api/slots`, `GET /api/slots/{name}`) expose  
`tune_effective`: `ctx_size`, `enable_thinking`, `reasoning_effort`,  
`reasoning_budget`, `vision`, `speculation`, `sources`,  
`reasoning_effort_unavailable` and `tune_unavailable`. So does  
`GET /api/slots/{name}/resolved` for a llama-server slot; the answer for a halogen  
slot, like that of every other non-llama runtime, has `argv: null` and no  
`tune_effective`. Sources are `profile`, `slot` or `profile-flags`;  
`reasoning_effort_unavailable` is a string or null. `tune_unavailable` maps each  
tune value the slot’s engine cannot run to the reason, and is `{}` on a  
llama-server slot; on a halogen slot it is  
`{"speculation": "ngram: this engine has no n-gram drafter (auto or off)"}`.  
This is an additive read contract. Model feasibility accepts a `slot` name per model  
row to resolve its profile plus tune for the candidate; explicit `ctx` wins.

Every `GET /api/slots/{name}/resolved` answer also carries `footprint`: `null` for a  
slot that is not on the halogen runtime and for a halogen slot with no model bound  
or no figure, otherwise `{"resident_mb", "advisory_cache_mb", "host_free_mb", "verdict"}`. `resident_mb` is the memory the engine’s layout holds once loaded (the  
checkpoint, KV pool, per-slot state, working memory, prompt cache and vision tower,  
never the lookup table); `advisory_cache_mb` is the page cache the lookup table  
wants, about 10 GiB, which is advised and never part of `resident_mb`;  
`host_free_mb` is the host’s free GTT pool, `null` when the host does not expose it.  
`verdict` is the advisory fit of the first against the third: `fits`, `tight`  
(within 10% of what is free), `exceeds`, `exceeds_total` (more than the whole pool)  
or `unknown` (no host reading), and `loaded` when the slot already holds the memory.  
It never blocks a load.

## Router groups

| Prefix | Auth | Purpose |
| --- | --- | --- |
| `/api/auth` | Mixed | `login`/`status`/`logout` are OPEN (you can’t require a cookie to obtain one); `require` (toggle enforcement) and `rotate` (mint a new admin/client key) are ADMIN; `exposure` (serialize this whole rule table) is ADMIN. |
| `/api/secrets` | ADMIN | Operator-managed secrets store. See [Secrets](https://forum.hal0.dev/t/rest-api-secrets-and-proxmox-credentials/128#secrets-apisecrets) for the routes and the server-side validation — dedicated section, not just a table row. |
| `/api/oauth` | Mixed | OAuth passthrough for Hermes skills (see [Run agents](https://forum.hal0.dev/t/run-agents-hal0-docs/40)): `GET /api/oauth/providers`, `POST /api/oauth/{provider_id}/start` (returns `authorize_url`), `GET /api/oauth/{provider_id}/status`, `DELETE /api/oauth/{provider_id}` (best-effort revoke, then delete the token), `POST /api/oauth/{provider_id}/client-secret`, and `GET /api/oauth/{provider_id}/callback`, the provider’s redirect target. The callback is OPEN (it carries no hal0 credential, only the provider’s single-use `state` nonce, which the route validates); every other route is ADMIN. Tokens and client secrets persist to `/etc/hal0/api.env` and are never returned. |
| `/api/settings` | ADMIN | Root config read/write/reload, JSON schema, apply-plan registry, schema-driven field metadata (`fields`), and a dry-run ChangeSet preview (`preview`) sharing the same diff the write path applies. |
| `/api/settings/models/store` | ADMIN | Model store path get/set/migrate (part of the `/api/settings` prefix). |
| `/api/settings/proxmox` | ADMIN | Proxmox integration config (host/token) + live cluster status. See [Proxmox integration](https://forum.hal0.dev/t/rest-api-secrets-and-proxmox-credentials/128#proxmox-integration-apisettingsproxmox) — no dedicated exposure rule; it inherits ADMIN from the generic `/api/settings` prefix. |
| `/api/slots` | CLIENT (list) / ADMIN (else) | Slot lifecycle — list, create, delete, restart, model swap, resolved-argv provenance by layer, metrics, capacity. Slot views carry `ctx_effective`, `ctx_source` and, for a slot file that still carries tuning keys, `conversion_blocked`. A slot write that carries a tuning key is refused with `slot.tuning_key_denied`; rebind a model with `PUT /api/slots/{name}/config` and `{"model": {"default": ...}}`. See [Slot lifecycle](https://forum.hal0.dev/t/slot-lifecycle-reference/58). |
| `/api/models` | CLIENT (GET) / ADMIN (mutations) | Model registry — list, pull, delete, inspect, scan, catalogue, update checks, and speculative-decoding drafters (`PUT /api/models/{id}` takes `{"draft": path|null}`; `DELETE /api/models/{id}/draft` unpairs and deletes the file; `POST /api/models/inspect-draft` fit-checks a drafter against a target before download; the pull body takes `draft_filename` and `draft_repo`; `POST /api/models/inspect` also returns `drafts` and `companion_repos`). The registry holds no image models: a pull of an image catalog id, and a create, update or add-from-path whose `path` is in ComfyUI’s models folder, answer 422 `model.image_asset`. `GET /api/models/health` is registered by `hardware.router`, not `models.router` — it’s mounted ahead of `/api/models` so the literal path wins over the `{model_id}` catch-all. `GET /api/models` also takes `installed`, `type`, `capability`, `device`, `q`, `fields`, `limit` and `offset` to filter, project and page the list (the reply gains `total`); with none of them it returns the full list. See [Model roster & benchmarks](https://forum.hal0.dev/t/model-roster-benchmarks-reference/55). |
| `/api/runner-images` | CLIENT (list/detail) / ADMIN (sync/pull) | Runner Image catalogue — sync from `images.json`, pull-job orchestration, tag removal and restart-affected-slots for GHCR-hosted runner images. |
| `/api/ports` | CLIENT (GET) / ADMIN (else) | Port allocation view. |
| `/api/hardware` | CLIENT (GET) / ADMIN (`/probe`) | Hardware probe read + on-demand re-probe. |
| `/api/stats/hardware` | CLIENT | Live hardware probe/stats, and the `memory` block the dashboard’s pool, RAM and per-owner memory figures are rendered from. See the [Memory budget API reference](https://forum.hal0.dev/t/memory-budget-api-reference/123) and the [Hardware matrix](https://forum.hal0.dev/t/hardware-matrix-reference/53). |
| `/api/stats` (`/slots`, `/requests`, `/throughput/history`, `/power`) | CLIENT (GET) / ADMIN (else) | Dispatcher rollups, throughput history, power stats. |
| `/api/status` | CLIENT | Version/build summary. Includes `build_sha`, the short git SHA of the tree the running process started from — `null` on a release (non-git) install. |
| `/api/health`, `/api/health/system` | OPEN | Shallow + deep liveness probes hit by the installer/systemd watchdog before any credential exists. |
| `/api/doctor` | ADMIN | `GET /api/doctor` — the same report card `hal0 doctor verify --json` prints, aggregated in-process from health/urls/system/memory/services, over HTTP. Classified ADMIN (not CLIENT) because it re-surfaces detail from ADMIN-only subsystems. |
| `/api/metrics` | CLIENT | JSON metrics snapshot. |
| `/api/metrics/prometheus` | OPEN | Prometheus scrape target. |
| `/api/features` | CLIENT (GET) | Feature-flag surface. `GET /api/features` is the only route `health.py` registers on this prefix — no mutating route is mounted, so `exposure.py`’s ADMIN-mutations rule is currently latent, not backing a real endpoint. The CLIENT rule is an exact match on `/api/features` only, so a hypothetical `GET /api/features/<sub>` would fall through to the prefix rule and resolve ADMIN. |
| `/api/system-info`, `/api/system-stats` | CLIENT | Host system info / fleet metrics over the SQLite tables (OBS-1). |
| `/api/capabilities` | ADMIN | Capability-slot configuration (embed/rerank/stt/tts/vision child slots). `GET` takes `section` (`backends`, `catalogs`, `selections`; comma list) and `summary=true` to shrink the payload; with neither it returns everything. |
| `/api/backends` | CLIENT (GET) / ADMIN (else) | Backend introspection (NPU/GPU-Vulkan/GPU-ROCm/CPU status) + NPU load/unload. |
| `/api/npu` | CLIENT (GET) / ADMIN (else) | NPU trio swap-status for the “swap incoming” dashboard banner. |
| `/api/providers` | ADMIN | Local provider entries (`providers.toml`). See [Providers, profiles & devices](https://forum.hal0.dev/t/providers-profiles-devices-reference/57). |
| `/api/upstreams` | ADMIN | Remote/cloud upstream entries (`upstreams.toml`), including credential write and connectivity test. Mounted on the same router as `/api/providers`. Each entry also carries `managed_by` (null, or `agent:hermes` for the per-profile Hermes upstreams `hermes` and `hermes.<profile>`), `pinned` and `owned_models`. An agent-managed upstream is read-only here: PATCH, DELETE and the credential write return 400 `upstream.protected`; change it through the agent. |
| `/api/stacks` | ADMIN | Stack bundles — list, create, apply, import/export, snapshot. |
| `/api/profiles` | CLIENT (GET) / ADMIN (mutations) | Profiles, the tune a slot launches a model with (flags, `ctx_size`, `enable_thinking`, `vision`, `speculation`, `runner`) — list (each entry carries `used_by`, the slot names), create, update, import/export (envelope schema 2). `speculation` is `auto`, `off` or `ngram`; a body that still sends the retired `mtp` is `422 profiles.field_renamed`. |
| `/api/benchmarks` | ADMIN | Benchmark queue, run status, control. See [Model roster & benchmarks](https://forum.hal0.dev/t/model-roster-benchmarks-reference/55). |
| `/api/chat-templates` | ADMIN | Chat-template catalog — bundled + operator-added templates. |
| `/api/logs` | ADMIN | Secret-redacted log tail. |
| `/api/mcp` | ADMIN | MCP server lifecycle/introspection — **not** tool invocation. See [MCP tools](https://forum.hal0.dev/t/mcp-tools-reference/54) for the full endpoint table. |
| `/api/comfyui` | ADMIN | ComfyUI generation-engine status and image-gen slot control, plus the image-model catalog. `GET /api/comfyui/models` lists the catalog with each model’s install state (`none`, `partial`, `complete`), the size still to download and in total (`missing_gb` and `approx_gb`; a GB is 2^30 bytes here, as in the memory budget) and its latest install job, plus a top-level `engine: {available, reason}` (`available` is `false` with a `reason` on a host with no ROCm lane; the catalog is listed either way); `POST /api/comfyui/models/{model_id}/install` starts a download and returns 202 with the job (it also clears a removal `DELETE` recorded), and `DELETE /api/comfyui/models/{model_id}` removes the model’s weight files and workflow and answers `{"removed": [...], "kept": [{"relpath": ..., "models": [...]}], "affected": [...]}`: a weight file another complete model loads stays and is listed in `kept` with the models that hold it, and `?include_shared=true` (default `false`) deletes it too, lists in `affected` the models that were complete and no longer are, and deletes their workflows. A delete records the removal, so a model that stays `complete` on shared weights is not given its workflow back at the next start. An install is refused with 404 `comfyui.model_unknown`, 409 `comfyui.install_running`, 409 `comfyui.model_installed` (every weight is on disk and its workflow is provisioned; a model with the weights but no workflow is accepted, downloads nothing and writes the workflow), 409 `comfyui.staging_cross_device`, 409 `comfyui.models_dir_not_writable` (a destination directory the hal0 user cannot write, or a models tree it cannot read, which `GET /models` and `DELETE` also answer; `error.details.path` names it) or 507 `comfyui.insufficient_disk`. A delete is refused with 404 `comfyui.model_unknown`, with 409 `comfyui.install_running` while that model’s install, or the install of a model sharing a weight file with it, is active, and with 409 `comfyui.remove_failed` when a file cannot be deleted (`error.details` has `removed`, the paths deleted before the failure, `failed_path` and `error`). `GET /api/comfyui/jobs` lists the latest install job of each model (`state`, `bytes_downloaded`, `bytes_total`), `POST /api/comfyui/jobs/{model_id}/cancel` stops the model’s install (`model_id` is the model’s id, not the job’s `id`; 404 `comfyui.job_not_found` when the model has no job), and `GET /api/comfyui/files` lists the weight files under the models tree with the catalog models that own each. `DELETE /api/comfyui/files/{subdir}/{relpath}` deletes one file no catalog model loads and answers `{"deleted": "<subdir>/<relpath>"}`; it is refused with 400 `comfyui.file_path_invalid` (`subdir` is not a model directory, or `relpath` is absolute, climbs out with `..`, has a hidden part or does not name a weight file: `.safetensors`, `.ckpt`, `.pt`, `.pth`, `.gguf`, `.bin` or `.sft`), 400 `comfyui.file_symlink`, 404 `comfyui.file_not_found`, 409 `comfyui.file_catalog_owned` (`error.details.models` names the owners) or 403 `comfyui.file_permission`. The model rows’ `fit` verdicts, `GET /api/comfyui/models/{model_id}/fit`, the arbiter’s `phase` fields, `progress`, and the GPU gate’s `gate` and `gate_refusal` (`POST /api/comfyui/gate-refusal` is the gate’s own) are in the [Image readiness API reference](https://forum.hal0.dev/t/image-readiness-api-reference/124). |
| `/api/install` | BOOTSTRAP | First-run wizard endpoints, including `GET /api/install/curated-models`. Open until an admin key is configured, then treated as ADMIN. |
| `/api/memory` | ADMIN | Two routers share this prefix: the `[memory.graph]` gate/status, and the Hindsight engine admin surface (banks, graph, recall/reflect, mental models, directives, async operations). Every `DELETE` under this prefix is pinned ADMIN by an explicit rule independent of the generic one (issues #1024/#1302 — a prior unauthenticated bank-delete cascade-dropped ~632 live records); bank-scoped deletes additionally require a `?confirm=` echo + dry-run preview. `GET /api/memory/graph/status` reports the engine target as `target` (`{kind: "slot"|"upstream", model, alias: "hal0/memory"}`), the upstream ids the engine can run on as `available_models` (`[{id, owned_by}]`) and the LLM hindsight-api really loads as `engine_llm` (`{provider, base_url, model, overridden, override_file, override_keys, source}`; read from the running hindsight-api process when hal0-api can (`source: "process"`), else from the unit plus `/etc/hal0/hindsight-llm.env` as systemd would apply them at the next start (`"unit+file"`, or `"unit"` when there is no readable file); `overridden` is `true` when that file sets the provider, base URL or model, and `null` when hal0-api can read neither the process nor the file, which the dashboard and CLI show as “cannot verify”). The status call also fills an empty upstream model cache (after a hal0-api restart) by fetching each provider’s catalog, retrying an offline provider at most once a minute, so `available_models` is populated on the first poll. It also reports `chunk_check`, the `ChunkAudit` (the shape of `GET /api/memory/graph/chunk-check`: `verdict`, `max_tokens`, `checked`, `skipped`, `conflicts`, `message`) of the banks listed in `failed_by_bank`, so a bank the engine refuses because its retain chunk size is not below `extraction_max_tokens` says so; it is `null` when no bank has failed operations or the engine cannot be read, and the audit is cached for 30 s. `PUT /api/memory/graph` accepts `extraction_model` (an upstream model id, or `null` for the local `extraction_slot`); an id no configured upstream advertises is refused with 422 `config.memory_graph_model_invalid` and neither a slot nor a model change restarts the engine. A `bank_id` under `/api/memory/banks/{bank_id}/…` may contain dots (`coding-agent::ultrareview-1.4`) as well as colons; a leading dot, a dot-only segment and any `..` return 400 `memory.invalid_bank`. |
| `/api/board` | ADMIN | Operator Board — a live, audited proxy to the Hermes kanban plugin: task/board CRUD, links, dispatch, and `WS /api/board/events`. Frozen FE↔BE contract (`ui/CONTRACTS.md` §4). Needs a running Hermes: `503 board.not_installed` (no Hermes agent), `503 board.unreachable` (dashboard not answering), `503 board.no_token` (token file missing); Hermes’s own errors come back as `board.upstream_error` (4xx passed through, 5xx as 502). |
| `/api/brain` | ADMIN | `POST /api/brain/chat` (SSE) — the primary hal0-brain platform-steward chat surface. Drives the full admin tool surface (slot mutations, model pulls, board writes), hence ADMIN. |
| `/api/updates` | ADMIN | Self-update: release-manifest check, prepare/commit/apply/rollback, channel get/set, post-update slot-drift report + restart. Job state is also kept under `/var/lib/hal0/update-jobs/`: a job read back from there after a hal0-api restart that is still `queued` or `running` was killed with the old process and reads `failed` with `error_code: "api_restarted"` and an `error` that says what was interrupted (“hal0-api restarted during the repair; the live release is unchanged” for a Hermes repair, “… during the build; …” for a Hermes converge, the one atomic builder, and “hal0-api restarted before this job finished; check `hal0 update status` before retrying” for any other job); a finished job reads as it was. |
| `/api/events` | ADMIN | Dashboard dock event feed — backfill + `GET /api/events/stream` (SSE). See the auth-tier note above. |
| `/api/activity` | ADMIN | Durable audit surface (SQLite-backed) — the source of truth for the dock’s activity rows (`kind=action`), with `/export` (csv/json) and `/stream` (SSE). See the auth-tier note above. |
| `/api/journal` | ADMIN | Unified journal panel — flattens `/api/events` into one envelope for the dashboard journal view. See the auth-tier note above. |
| `/api/images` | ADMIN | Cache for PNGs written by `POST /v1/images/generations`. ADMIN because a cached-image URL could otherwise leak a prompt via filename. |
| `/api/agents` | ADMIN | Bundled-agent lifecycle (install/uninstall/list/status), personas, service restart, Hermes gateway restart and Hermes repair (see the [Agents API reference](https://forum.hal0.dev/t/agents-api-reference/118)), memory stats, and Hermes command approvals (`GET`/`PUT /api/agents/hermes/command-approvals`). The command-approvals `PUT` also refuses a request whose `X-hal0-Agent` header names an agent. `POST /api/agents/hermes/reconcile-upstreams` re-registers the Hermes profiles as upstreams on demand (it runs the profile API-key sweep first) and returns `{ok: true, ...report}`; 404 `agent.not_installed` when Hermes is not installed. hal0-api also reconciles every 120 s and after an agent install or uninstall. The agent chat proxy is gone: `/api/agents/{id}/events`, `/submit` and `/session/handshake|create|resume|history` return 404. `GET /api/agents/{name}/overview` is the state behind the Hermes and Pi cards (see the [Agents API reference](https://forum.hal0.dev/t/agents-api-reference/118)). |
| `/api/agent/approvals` | ADMIN | Gated-tool approval inbox — every method (GET list, POST approve/deny, GET `/events` SSE) is ADMIN; `exposure.py`’s rule on this prefix carries no method filter. Approve and deny also check the caller themselves at every posture: a key of any tier gets `403 auth.forbidden`, and only a session cookie decides ([who can decide](https://forum.hal0.dev/t/agents-hal0-docs/23#who-can-decide-an-approval)). |
| `/api/bridge` | BRIDGE (`GET /me`) / ADMIN (else) | Agent bridge clients for remote coding agents ([ADR-0043](https://github.com/Hal0ai/hal0/blob/main/docs/adr/0043-agent-bridge-and-bridge-keys.md)) and the runs they delegate to Hermes; every route, the `bridge.*` error codes, the output paging rule and which routes work while `[bridge].enabled` is false are in the [Agent bridge API reference](https://forum.hal0.dev/t/agent-bridge-api-reference/131). |
| `/api/config` | Mixed | `GET /api/config/urls` (dashboard bootstrap hostnames) is OPEN; the rest of this prefix is ADMIN (may hold config-derived secrets). |
| `/api/meta` | CLIENT (GET) / ADMIN (else) | `GET /api/meta/enums` — the canonical device/backend/capability vocabulary from `hal0.model_meta`. |
| `/api/hf` | CLIENT (GET) / ADMIN (else) | HuggingFace Hub search proxy backing the dashboard’s “Search HF” button. |
| `/api/services` | ADMIN | `services_health.router` (`GET /api/services/health`, stable overview iterating `hal0.services.registry.SERVICES` — comfyui, hermes, hindsight, openwebui) and `services_routes.router` (full per-service detail + lifecycle actions + mDNS) share this prefix. |
| `/api/dashboard/plugins` | ADMIN | Hermes dashboard-plugin manifest (kanban plugin discovery for `<AgentView>`). |
| `/api/openrouter` | ADMIN | OpenRouter OAuth callback scaffold — only mounted when `HAL0_OPENROUTER_OAUTH_ENABLED` is truthy; 501 placeholder otherwise absent from the route table entirely. |

`POST /api/memory/add` returns 503 `memory.engine_unavailable` when the  
memory engine connection fails or connecting times out. The message asks the  
caller to retry shortly; `error.details.retry_after_s` and `Retry-After` are  
both 5 seconds. An error response from the engine is not reclassified as  
engine unavailability.

Every prefix above is backed by at least one route confirmed directly against  
`src/hal0/api/routes/` and the mount list in `src/hal0/api/ __init__.py`. This brings  
documented coverage to every `/api/*` prefix currently mounted (previously 18 of  
roughly 43). `WS /v1/realtime` is documented separately — see  
[Realtime API reference](https://forum.hal0.dev/t/realtime-api-reference/106).

## Secrets and Proxmox

The `/api/secrets` routes and their server-side validation, and the  
`/api/settings/proxmox` routes, are documented in  
[REST API: secrets and Proxmox credentials](https://forum.hal0.dev/t/rest-api-secrets-and-proxmox-credentials/128).

## Agent overview, restart and repair

`GET /api/agents/{name}/overview` (the state behind the Hermes and Pi cards),  
`POST /api/agents/hermes/restart`, `POST /api/agents/hermes/gateway/restart` and  
`POST /api/agents/hermes/repair` (a tracked job, served by  
`GET /api/updates/status/{id}`) are documented in the  
[Agents API reference](https://forum.hal0.dev/t/agents-api-reference/118).

> [@](#):
>
> **Note**
> 
> This page could not verify the `/v1/*` OpenAI-compatible inference surface in depth —  
> see [OpenAI-compatible API reference](https://forum.hal0.dev/t/openai-compatible-api-reference/46),  
> [Streaming reference](https://forum.hal0.dev/t/streaming-reference/49), and  
> [Realtime API reference](https://forum.hal0.dev/t/realtime-api-reference/106) for those, verified in  
> separate passes.
