--- name: hermes-api-server-setup description: "Set up Hermes Agent built-in OpenAI-compatible API server on a custom port with external access. Includes the critical extra nesting pitfall." version: 1.0 category: devops --- # Hermes API Server Setup Hermes has a built-in OpenAI-compatible API server (`gateway/platforms/api_server.py`) that exposes endpoints like `/v1/chat/completions`, `/v1/responses`, `/v1/models`, `/health`, etc. Any OpenAI-compatible frontend (Open WebUI, LobeChat, LibreChat, ChatBox, etc.) can connect. ## Critical Pitfall - extra Nesting **Host, port, key, cors_origins MUST be nested under `extra:` in config.yaml.** If placed directly under the platform name, `PlatformConfig.from_dict()` silently ignores them (it only reads `enabled`, `token`, `api_key`, `home_channel`, `reply_to_mode`, `extra`). WRONG - binds to 127.0.0.1 only (silently ignores host/port): ```yaml platforms: api_server: enabled: true host: "0.0.0.0" port: 8642 key: "my-secret" ``` CORRECT - binds to 0.0.0.0: ```yaml platforms: api_server: enabled: true extra: host: "0.0.0.0" port: 8642 key: "my-secret" cors_origins: - "*" ``` ## Defaults - `DEFAULT_HOST`: `127.0.0.1` (localhost only - NOT externally accessible) - `DEFAULT_PORT`: `8642` ## Setup Steps 1. Add the `platforms:` block at the TOP LEVEL of `~/.hermes/config.yaml` (not nested under `display:` or `agent:`). 2. Ensure host is `0.0.0.0` for external access, set a `key` for API auth, and `cors_origins: ["*"]` for browser clients. 3. Restart the gateway: ```bash systemctl --user restart hermes-gateway.service ``` 4. Verify: ```bash # Check port is listening on 0.0.0.0 ss -tlnp | grep 8642 # Health check curl http://localhost:8642/health # Model list (auth required) curl http://localhost:8642/v1/models -H "Authorization: Bearer YOUR_KEY" ``` 5. If `ss` shows `127.0.0.1:8642` instead of `0.0.0.0:8642`, the `extra:` nesting is wrong - recheck step 1. ## Client Connection | Field | Value | |------------|----------------------------------| | Base URL | `http://YOUR_HOST:8642/v1` | | API Key | Value of `extra.key` in config | | Model ID | `hermes-agent` (or active profile name) | ## Available Endpoints - `GET /health` - Health check - `GET /v1/models` - Model list - `POST /v1/chat/completions` - OpenAI Chat Completions (SSE streaming supported) - `POST /v1/responses` - OpenAI Responses API (stateful) - `GET /v1/responses/{id}` - Retrieve stored response - `DELETE /v1/responses/{id}` - Delete stored response - `POST /v1/runs` - Async run (returns 202 + run_id) - `GET /v1/runs/{id}/events` - SSE stream of run lifecycle events ### Session Management Endpoints - `GET /v1/sessions` - List sessions (query: `limit`, `offset`, `source`, `include_children`, `order_by_last_active`) - `DELETE /v1/sessions/{session_id}` - Delete a session and its messages - `POST /v1/sessions/prune` - Delete ended sessions older than N days (body: `older_than_days`, `source`) - `GET /v1/capabilities` - Advertises all endpoints including sessions (third-party UIs use this for discovery) Session IDs are validated (hex + underscore/dash only) to prevent path traversal. All session endpoints require the same `Authorization: Bearer KEY` as other endpoints. ## Troubleshooting: API Server Unresponsive If the API server returns connection refused or times out: 1. **Check if gateway service is running:** ```bash systemctl --user status hermes-gateway.service ``` 2. **Check if port is listening:** ```bash ss -tlnp | grep 8642 ``` No output = gateway not running or API Server not started. 3. **Check gateway logs for startup errors or SIGTERM:** ```bash cat ~/.hermes/logs/gateway.log ``` Look for `Received SIGTERM` (systemd or user killed it) or `api_server connected` (started OK). 4. **Restart gateway:** ```bash systemctl --user restart hermes-gateway.service # or: hermes gateway start ``` 5. **Verify after restart:** ```bash sleep 5 && curl -s http://localhost:8642/health ``` Common cause: Gateway received SIGTERM (e.g. from `hermes gateway stop`, system reboot, or OOM). The API Server is NOT a separate process — it runs inside the gateway. If the gateway dies, API Server dies with it. ## Environment Variable Alternative Instead of config.yaml, you can also use env vars (checked at gateway startup): - `API_SERVER_ENABLED=true` - `API_SERVER_HOST=0.0.0.0` - `API_SERVER_PORT=8642` - `API_SERVER_KEY=my-secret` - `API_SERVER_CORS_ORIGINS=*` - `API_SERVER_MODEL_NAME=custom-name` These are read in `gateway/config.py` and take effect if `API_SERVER_ENABLED=true` OR `API_SERVER_KEY` is set.