Skip to content

The Local API

The companion daemon is a normal HTTP server. It exposes the account it is signed in to — connections, codes, invitations, tags, presets, open links, organisation management — as JSON over /api/v1/.

This is how a script, a back-office system or a machine account uses Trusted Codes without a person and a phone. The daemon holds the session and the keys; the caller holds a token and speaks plain HTTP.

The default address is http://127.0.0.1:19821. That is the single server entry in the OpenAPI document, and it is the fixed port — loopback only.

Two environment variables move it: TCC_HTTP_BIND and TCC_HTTP_PORT. Headless callers should respect both rather than assuming the default.

The port being fixed is deliberate. It can be allowlisted once and then left alone.

The daemon serves two OpenAPI documents at two paths, for two audiences.

GET /api/v1/openapi.json is the typed REST document — the routes these pages describe. It is generated from the Rust structs that serialise the responses, so a field cannot be spelled one way in the document and another way on the wire. The same document is committed in the repository, and the companion web UI’s TypeScript response types are generated from it.

GET /openapi.json, at the unprefixed path, is a different thing: the catalogue of the daemon’s MCP tools, for MCP clients and custom GPTs. The MCP section covers that surface.

GET /api/v1/docs serves an interactive page rendering the REST document. Both that page and /api/v1/openapi.json are public — no token.

GET /health (also GET /api/v1/health) is the only route with no authentication at all. Use it to answer “is the daemon up”.

GET /api/v1/status needs a token and answers more: whether the daemon is authenticated, whether the device is registered, whether the global key is active, the environment name it started with, the backend it actually resolved, the daemon version, how many connections are cached, the last sync time, and a one-line summary.

The environment name and the resolved backend are separate fields on purpose. A daemon started with one environment name can be pointed elsewhere by an override, and only the resolved value tells you where it is really talking.

Some of those fields are null for a machine-to-machine caller, which holds no local cache and is not the daemon’s own user. A null there means “not applicable”, not “unknown”.

The tool and MCP endpoints are registered twice: once at a flat path for older clients, and once under /api/v1/. New integrations should target the prefixed form. The unprefixed paths stay stable through the v1 lifecycle and are only removed when /api/v2/ ships.

The REST routes in these pages exist under /api/v1/ only.