API overview
Where the daemon binds, how each route is authorised, the rate-limit buckets, and what an error body looks like.
The daemon serves its HTTP API on http://127.0.0.1:3141, loopback only. Start it with
grith daemon start.
Shape of the surface
Every REST route is nested once under /api. The WebSockets are not - they sit at the
root, at /ws/live and /ws/supervisor/{id}. Paths are written {id} here; the router
uses axum's :id internally.
Three authorisation tiers
| Tier | Routes | What you send |
|---|---|---|
| Open read | /api/health, /api/tier, /api/license/status, /api/proxy/status, /api/onboarding/status, /api/sync/status, /api/notifications/channels, /api/notifications/status | Nothing. Never token-gated. |
| Sensitive read | /api/digest, /api/canaries, /api/audit*, /api/config, /api/analytics/v2/*, /api/policies*, /api/sync/configs, /api/inventory/{id}, /api/sessions/{id}/listener-rewrites | x-grith-csrf: <dashboard token>, once a token exists. |
| Write | Every dashboard POST, PUT and DELETE | x-grith-csrf: <dashboard token>. |
The five /api/supervisor/* routes take both layers: the token on every method, the CSRF
header on the three mutations. The daemon IPC routes are a separate surface entirely, on a
bearer token - see Daemon IPC.
Three writes carry their own proof instead and skip the CSRF layer:
POST /api/digest/{id}/webhook-review (single-use nonce),
POST /api/server/shutdown (daemon bearer token) and
POST /api/dashboard/pair (the pairing code itself).
Open does not mean free. GET /api/sync/status and the two /api/notifications reads take
no token but still return 403 FEATURE_GATED on Community - the gate is inside the
handler. See Notifications.
Full details on the secrets and the pairing flow are on Authentication.
Rate limits
Each bucket is a sliding one-second window, counted after the auth layers, so an unauthenticated request never spends your quota.
| Bucket | Requests per second | Applies to |
|---|---|---|
| General | 100 | Open reads, sensitive reads, supervisor GETs |
| Write | 10 | Every dashboard write |
| ProxyTest | 20 | POST /api/proxy/test |
| Ipc | 10000 | The daemon IPC routes |
The first three are configurable under [server.rate_limit]; ipc_rps is not exposed in
config. Over the limit is 429 with a Retry-After header and a body whose code is
RATE_LIMITED.
Error bodies
Most failures are a two-field envelope of error and code. A tier refusal is 403 and
carries enough to render an upgrade prompt:
{
"error": "usage_analytics requires a Pro subscription",
"code": "FEATURE_GATED",
"feature": "usage_analytics",
"current_tier": "community",
"required_tier": "Pro",
"upgrade_url": "https://grith.ai/pricing"
}upgrade_url is the licence's billing portal when one is known, otherwise
https://grith.ai/pricing. The analytics routes add one status of their own:
503 ANALYTICS_UNAVAILABLE, returned when the process holding the audit database is an
older grith and cannot serve the projection.
Binding beyond loopback
server.localhost_only defaults to true and a non-loopback peer gets
403 LOCALHOST_ONLY. The daemon refuses to start if you turn it off without also setting
an API key. Native TLS is available under [server.tls]; see
Server config and
Reverse proxy and TLS.
Where to go next
- Authentication - the three secrets and the pairing flow
- Health and tier - liveness, plan, config
- Digest - read the queue and decide items
- WebSocket - the live event stream
- Daemon IPC - the bearer-token surface the CLI uses