Custodian
ReferenceHTTP API

Using the HTTP API

How to call the HTTP API — the bearer token every route needs, the vault each request reaches, the shape of an authentication failure, and how to confirm which principal a token is.

Custodian exposes an HTTP API under /v1, and an agent-facing endpoint at /mcp. This page covers what is true of every call: the credential each one carries, the vault it reaches, and the response a request that cannot be authenticated gets. Each route has its own page under this section.

The values your machine seals

These pages describe the HTTP wire format: the exact requests each route accepts and the responses it returns. Some requests carry bytes your own machine must produce first — a field value sealed on your device, a record's wrapped key, a blind index to search by, a recovery blob. This reference shows where each of these belongs in a request, written as <base64>; it does not restate how to produce them.

Those bytes are produced on an enrolled device: a machine that holds a device key and, through it, a wrapped copy of the vault's master key. The custodian command line produces them, and so does any client that implements the same formats. If you have not enrolled a device yet, start from the command line.

Every request carries a bearer token

Every /v1 route, and /mcp, authenticates the caller from an Authorization header carrying a principal token:

Authorization: Bearer <token>

A principal is one identity in one vault — an operator, a service or an agent. A token is minted once, when the principal is created, and is the only thing that authenticates as it. You get a vault's first token from bootstrapping the vault; an operator mints the rest from /v1/principals.

The one route that does not carry a principal token is POST /v1/vaults, which creates a vault before any principal for it exists; it authenticates against the deployment bootstrap credential instead. See Vaults.

Each request reaches one vault, fixed by the credential

A request is scoped to the vault its principal belongs to, and to no other. You never name a vault in a request — the vault is taken from the credential, never from a parameter, a header or a path segment. A record, principal or grant in another vault is invisible: a request for it comes back as though it did not exist, so one tenant cannot tell whether another exists.

An agent principal reaches its vault only through /mcp. Every /v1 route refuses an agent with the agent_forbidden_on_v1 code before it does any work.

When a request cannot be authenticated

A request whose token is missing, unknown, unverifiable or belongs to a revoked principal is refused with one fixed response, and no work is done — no transaction is opened and nothing is read or written:

{
  "code": "unauthenticated",
  "message": "The request could not be authenticated."
}

The message is the same for every one of those reasons, by design: the response does not disclose which of them applied. The full shape every refusal takes, and the closed set of code values, is on the Errors page.

When the vault is rotating or its keys are destroyed

Two vault states cut across every route. While a vault is rotating its master key, and after its keys are destroyed, any /v1 route may refuse the request with a coded error envelope and a 409 Conflict — rather than the unexplained error it returned before these states were made legible.

While a rotation is running:

{
  "code": "vault_rotation_in_progress",
  "message": "This vault is rotating its master key, and refuses this request until that rotation ends.",
  "details": { "started_at": "2026-08-28T10:00:00Z" }
}

After the vault's keys are destroyed:

{
  "code": "vault_keys_destroyed",
  "message": "This vault's keys have been destroyed, and it refuses this request.",
  "details": { "destroyed_at": "2026-08-28T10:10:00Z" }
}

Branch on the code: vault_rotation_in_progress clears once the rotation is completed, suspended or discarded, so the request can be retried then; vault_keys_destroyed is permanent. The rotation routes themselves are exempt from the rotation refusal, so a rotation can still be read and carried forward while it holds every other caller out.

GET /v1/whoami — which principal is this token?

GET /v1/whoami names the calling principal and its vault. It takes no parameters: the answer is settled entirely by the credential presented. Use it to confirm which principal a token is.

GET /v1/whoami
Authorization: Bearer <token>
{
  "principal": {
    "slug": "liberty-agent",
    "kind": "service",
    "revoked": false
  },
  "vault": {
    "id": 42,
    "slug": "acme",
    "display_name": "Acme Insurance",
    "region": "af-south-1",
    "created_at": "2026-08-12T09:00:00Z",
    "keys_destroyed": false,
    "recovery_configured": true
  }
}

The response carries the principal's slug, kind and revocation state, and the vault's public fields. recovery_configured reports whether the vault holds a recovery envelope; it never returns the envelope itself. No grant, token, selector or key identifier is returned.

On this page