Kms API Reference

REST API endpoints

Every path is /v1/kms/*. There is no /api prefix and no v2 — the surface below is the whole HTTP API.

Hosts

One KMS per org. The env (dev / test / main / default) is a field on each secret, not a hostname — there are no kms.dev.* / kms.test.* hosts.

OrgHost
Luxhttps://kms.lux.cloud
Hanzo, Zoo, Parshttps://kms.hanzo.ai
any (aggregate)https://api.hanzo.ai

Authentication

Machine identity, exchanged for a short-lived bearer:

curl -X POST https://kms.lux.cloud/v1/kms/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"clientId": "...", "clientSecret": "..."}'
# -> {"accessToken": "...", "expiresIn": 3600}

Send it as Authorization: Bearer $TOKEN on every request below. The token is org-scoped: it reaches /v1/kms/orgs/<its own org>/* and nothing else.

Secrets

A secret is addressed by (org, path, name, env). In the URL, path and name form one trailing segment list that the server splits at its last slash — so gateway/routes means path gateway, name routes. Escape each segment individually; escaping the joined string encodes the separators away and the server reads one long name.

MethodPathDescription
GET/v1/kms/orgs/{org}/secrets?path=&env=List secret names under a path
GET/v1/kms/orgs/{org}/secrets/{path}/{name}?env=Read one value
POST/v1/kms/orgs/{org}/secretsCreate or replace
DELETE/v1/kms/orgs/{org}/secrets/{path}/{name}?env=Delete

env defaults to default when omitted.

Create and replace are the same call — one upsert, not a separate create and update:

{"path": "gateway", "name": "routes", "env": "main", "value": "..."}

Responses:

{"names": ["routes", "upstreams"]}   // list
{"secret": {"value": "..."}}         // read

Health

MethodPathDescription
GET/healthz, /healthLiveness
GET/v1/kms/healthz, /v1/kms/healthSame, under the versioned prefix

Returns {"service":"kms","status":"ok"}. status is degraded when MPC is configured but unreachable — still HTTP 200, so a working secrets surface is not flapped out of rotation.

Keys (MPC-backed)

/v1/kms/keys/* is registered only when the deployment is configured with MPC. On a secrets-only KMS these paths return 404; when MPC is configured but unreachable they return 503 with {"error":"mpc unreachable","mode":"secrets-only"}. KMS never holds private key material — signing is t-of-n in the MPC cluster.

MethodPathDescription
POST/v1/kms/keys/generateGenerate a validator key set (MPC DKG)
GET/v1/kms/keysList key sets
GET/v1/kms/keys/{id}Get one key set
POST/v1/kms/keys/{id}/signThreshold sign
POST/v1/kms/keys/{id}/rotateReshare
GET/v1/kms/statusKMS + MPC cluster status

Errors

The service answers in JSON and never in HTML — including 404s, redirects and internal errors. An unmatched path returns:

{"message": "not found", "path": "/whatever/you/asked/for"}

That matters more than it looks. Earlier builds embedded a console SPA under a root catch-all and answered every unmatched path with 200 text/html, so a wrong URL read as a decode failure rather than a missing route — and several clients were written against paths this service has never served, one of them documenting the wrong path as verified. If a /v1/ path hands you HTML, you are not talking to this service.