Daemion docs

How do I manage the gateway system?

Auth: Bearer token from device pairing
Base URL: http://localhost:3001
All examples tested against live gateway

These endpoints manage the gateway process itself: liveness checks, device pairing, daemon lifecycle, cost reporting, filesystem browsing, and UTCP tool discovery for external agents. Most require a bearer token; /health and POST /pair are unauthenticated.


How do I check if the gateway is running?

GET /health

Liveness check. No authentication required.

bash
Verified
json

{ “status”: “ok”, “uptime”: 3724.5 }

uptime is the number of seconds the gateway process has been running.


How does device pairing work?

Daemion uses a two-step OTP exchange to pair a new device. The mobile app (or any client) scans a QR code that encodes the gateway URL and a short-lived OTP. Presenting that OTP to POST /pair returns the long-lived bearer token used for all subsequent requests.

Step 1 — Generate a new OTP

GET /pair/refresh Auth required

Generate a fresh one-time pairing code. Requires an existing bearer token (i.e. called from an already-paired device).

bash
Verified

curl “http://localhost:3001/pair/refresh
-H “Authorization: Bearer $DAEMION_TOKEN”

json

{ “code”: “847291”, “expiresAt”: “2026-03-31T08:05:00.000Z” }

Step 2 — Exchange OTP for bearer token

POST /pair

Exchange a one-time pairing code for the gateway bearer token. No authentication required — this is how a new device gets its token.

Parameter Type Description
otp REQUIRED string The 6-digit one-time code from /pair/refresh or the QR code.
bash
Verified

curl -X POST http://localhost:3001/pair
-H “Content-Type: application/json”
-d ’{ “otp”: “847291” }’

json

{ “token”: “eyJ…” }

Store this token as $DAEMION_TOKEN and include it as Authorization: Bearer $DAEMION_TOKEN on all subsequent requests.

The bearer token is a device pairing credential, not a user login. It connects a browser or phone to your local gateway. There is no user account or password — authentication is handled by the Claude CLI session on the gateway host.


How do I stop or restart the daemon?

POST /daemon/stop Auth required

Gracefully shut down the gateway process. Returns 200 immediately; the process exits ~200ms later.

bash
Verified

curl -X POST http://localhost:3001/daemon/stop
-H “Authorization: Bearer $DAEMION_TOKEN”

json

{ “status”: “stopping” }

POST /daemon/restart Auth required

Exit the gateway process so the process manager (launchd on macOS, systemd on Linux) can restart it. Returns 200 immediately; the process exits ~500ms later.

bash
Verified

curl -X POST http://localhost:3001/daemon/restart
-H “Authorization: Bearer $DAEMION_TOKEN”

json

{ “status”: “restarting” }

Both endpoints rely on an external process manager to handle the restart. On macOS, launchd automatically relaunches the service. On Linux, systemd does the same. If no process manager is running, restart is equivalent to stop.


How do I resync built-in extensions without restarting?

POST /reseed Auth required

Re-sync built-in extensions from disk and refresh the global skill context. Does not restart the gateway process.

bash
Verified

curl -X POST http://localhost:3001/reseed
-H “Authorization: Bearer $DAEMION_TOKEN”

json

{ “status”: “reseeded” }

Use this after adding or editing agents or skills on disk (e.g. in ~/.daemion/agents/). The gateway picks up the changes immediately without a restart.


How do I check usage costs?

GET /costs Auth required

Return aggregated cost totals and breakdowns by agent and model.

Parameter Type Description
period string Time window: today (default), week (last 7 days), or month (last 30 days).
bash
Verified

curl “http://localhost:3001/costs?period=week
-H “Authorization: Bearer $DAEMION_TOKEN”

json

{ “today”: 0.042, “week”: 1.18, “month”: 4.75, “by_agent”: [ { “agent_id”: “opus”, “name”: “opus”, “cost”: 0.91 }, { “agent_id”: “sonnet”, “name”: “sonnet”, “cost”: 0.27 } ], “by_model”: [ { “model”: “claude-opus-4-5”, “cost”: 0.91 }, { “model”: “claude-sonnet-4-5”, “cost”: 0.27 } ] }

All cost values are in USD. The period query parameter filters by_agent and by_model breakdowns; the today, week, and month totals are always returned regardless of the period filter.


How do I browse the filesystem?

Both filesystem endpoints default to os.homedir() when no path is provided. Any bearer-token holder can list and search your home directory. Scope the path parameter explicitly, and restrict who holds the gateway token.

GET /filesystem/ls Auth required

List subdirectories at a path. Only returns directories (not files), and hides dotfiles. Path must be within the gateway host's home directory.

Parameter Type Description
path string Absolute path to list. Defaults to os.homedir(). Must be within the home directory — paths outside return 403.
bash
Verified

curl “http://localhost:3001/filesystem/ls?path=/home/user/projects
-H “Authorization: Bearer $DAEMION_TOKEN”

json

{ “path”: “/home/user/projects”, “entries”: [ { “name”: “daemion”, “path”: “/home/user/projects/daemion”, “isDirectory”: true }, { “name”: “agent-desktop”, “path”: “/home/user/projects/agent-desktop”, “isDirectory”: true } ] }

GET /filesystem/search Auth required

Search for files and directories by name under a root path. Searches up to 3 levels deep, returns up to 15 results. Path must be within the home directory.

Parameter Type Description
q REQUIRED string Search query. Minimum 2 characters. Matched case-insensitively against entry names.
path string Root path to search under. Defaults to os.homedir(). Must be within the home directory.
bash
Verified

curl “http://localhost:3001/filesystem/search?q=claude&path=/home/user
-H “Authorization: Bearer $DAEMION_TOKEN”

json

{ “query”: “claude”, “root”: “/home/user”, “results”: [ { “name”: “daemion”, “path”: “/home/user/projects/daemion”, “isDirectory”: true }, { “name”: “.claude”, “path”: “/home/user/.claude”, “isDirectory”: true } ] }

Both endpoints resolve the path and reject any .. traversal attempts. Dotfiles and node_modules are excluded from search results.


How do external agents discover available tools?

GET /utcp Auth required

Return a UTCP (Universal Tool Calling Protocol) manifest listing all tools the gateway exposes. Used by external agents (Pi, BeadBoard, etc.) to discover what operations are available.

bash
Verified

curl “http://localhost:3001/utcp
-H “Authorization: Bearer $DAEMION_TOKEN”

The manifest follows the UTCP schema: a list of tool definitions with name, description, input schema, and endpoint binding. External agents can import this manifest to call Daemion tools programmatically without hardcoding endpoint paths.


How do I list available agent modes?

GET /modes Auth required

List the built-in execution modes (plan, act, quick, complex, etc.). Requires authentication.

bash
Verified

curl http://localhost:3001/modes
-H “Authorization: Bearer $DAEMION_TOKEN”


Q Is /health authenticated?
No. GET /health and POST /pair are the only unauthenticated endpoints. Everything else, including GET /modes, requires a bearer token.
Q What happens to running app dev servers when the daemon stops?
All running Vite dev servers are killed. The gateway registers SIGTERM and SIGINT handlers that clean up all child processes before exit.
Q Does reseed require a restart?
No. POST /reseed re-syncs disk agents, skills, and built-in extensions in place — the gateway process keeps running. This is the right way to pick up changes to ~/.daemion/agents/ without downtime.
Q Can I access the filesystem outside my home directory?
No. Both /filesystem/ls and /filesystem/search resolve paths and reject anything outside os.homedir() with a 403. This is enforced server-side regardless of what path parameter is supplied.
Q What does the period param do for /costs?
It filters the by_agent and by_model breakdowns. The three top-level totals (today, week, month) are always included in the response.

What can go wrong

Common errors

401 {"error": "unauthorized"} — Bearer token missing or invalid. Re-pair the device via POST /pair with a fresh OTP from GET /pair/refresh.

401 {"error": "Invalid or expired code"} — The OTP passed to POST /pair is wrong or has expired. OTPs are short-lived — generate a new one with GET /pair/refresh and retry immediately.

429 {"error": "Too many attempts. Wait a minute and try again."} — Too many failed OTP attempts. The rate limiter will reset after 60 seconds.

501 {"error": "Pairing not available — gateway started without token"} — The gateway was started without a token configured. Restart it with a token set in ~/.daemion/.gateway-token.

403 {"error": "path outside home directory"} — The path param on /filesystem/ls or /filesystem/search resolved to a location outside os.homedir(). Use an absolute path within your home directory.

403 {"error": "permission denied"} — The gateway process does not have read access to the requested path. Check filesystem permissions.

400 {"error": "query too short (min 2 chars)"} — The q param on /filesystem/search must be at least 2 characters.


What’s next?