# HTTP API

`KMS.API` exposes a minimal JSON API for backend KMS clients.

This API is an admin/backend surface. Do not expose it directly to browsers or the public internet.

Most applications should use `KMS.Client.Remote` instead of hand-writing HTTP calls.

## Authentication

Health endpoints do not require a token.

All `/api/*` endpoints require:

```text
Authorization: Bearer <KMS_API_TOKEN>
```

If `config :kms, :api` has no `:api_token`, API routes fail closed:

```json
{"error":"api_token_required"}
```

## Common request shape

Most POST bodies are JSON objects.

Operations that accept options use an `opts` object:

```json
{
  "alias": "owner:alice",
  "cleartext": "secret",
  "opts": {"aad": "document:123/body"}
}
```

`opts.session` may carry a KMS session secret for principal-scoped authorization.

## Common responses

Success returns `200` with an operation-specific JSON key.

Errors return JSON:

```json
{"error":"not_found"}
```

Validation errors return:

```json
{"error":"invalid_changeset","details":{}}
```

Common statuses:

| Status | Meaning |
|---:|---|
| 200 | Success |
| 400 | Invalid JSON/body shape |
| 401 | Missing or wrong API token |
| 404 | Not found |
| 422 | Invalid changeset |
| 503 | API token missing or readiness failed |

## Health

| Method | Path | Auth | Description |
|---|---|---|---|
| GET | `/health` | no | Cheap compatibility check. |
| GET | `/health/live` | no | Liveness check. |
| GET | `/health/ready` | no | Readiness check for DB, RMK access, and API token config. |

## Encryption

| Method | Path | Body | Response |
|---|---|---|---|
| POST | `/api/encrypt` | `alias`, `cleartext`, `opts` | `encrypted` |
| POST | `/api/decrypt` | `alias`, `encrypted`, `opts` | `cleartext` or `cleartexts` |
| POST | `/api/rmk_pdk/encrypt` | `cleartext`, `secret`, `opts` | `encrypted` |
| POST | `/api/rmk_pdk/decrypt` | `encrypted`, `secret`, `opts` | `cleartext` |

Example:

```sh
curl -fsS \
  -H "authorization: Bearer $KMS_API_TOKEN" \
  -H "content-type: application/json" \
  -d '{"alias":"owner:alice","cleartext":"secret","opts":{"aad":"document:123/body"}}' \
  https://kms.internal.example.com/api/encrypt
```

## KEKs

| Method | Path | Body | Response |
|---|---|---|---|
| POST | `/api/keks/create` | `alias`, `opts` | `kek` |
| POST | `/api/keks/tags` | `alias`, `opts` | `tags` |
| POST | `/api/keks/aliases_by_tag` | `tag`, `opts` | `aliases` |
| POST | `/api/keks/add_tags` | `alias`, `tags`, `opts` | `tags` |
| POST | `/api/keks/remove_tags` | `alias`, `tags`, `opts` | `tags` |
| POST | `/api/keks/rewrap_for_principal` | `alias`, `target_kind`, `target_ref`, `opts` | `ok` |
| POST | `/api/keks/rewrap_for_passphrase` | `alias`, `passphrase`, `opts` | `wrapping_id` |
| POST | `/api/keks/revoke_passphrase` | `alias`, `wrapping_id`, `opts` | `ok` |

KEK metadata includes `protection: "rmk" | "session"`, never key material. New aliases created with `opts.session` are session-protected. Passphrase sharing/revocation requires the principal's active session, `kek.rewrap` permission, and a usable recipient envelope. See [Factor-bound aliases](use_cases/factor_bound_aliases.md) for the complete flow and compatibility limits.

## Principals

| Method | Path | Body | Response |
|---|---|---|---|
| POST | `/api/principals/get` | `kind`, `ref`, `opts` | `principal` |
| POST | `/api/principals/list` | `opts` | `principals` |
| POST | `/api/principals/create` | `attrs`, `opts` | `principal` |
| POST | `/api/principals/update` | `kind`, `ref`, `attrs`, `opts` | `principal` |
| POST | `/api/principals/delete` | `kind`, `ref` | `principal` |

When `opts.session` is supplied to principal creation, it must authorize `principal.create` against the new principal's reference/tags. Omitting it remains trusted bootstrap/admin execution; a malformed supplied session is not treated as absent.

## Principal factors

| Method | Path | Body | Response |
|---|---|---|---|
| POST | `/api/principal_factors/get` | `id` | `principal_factor` |
| POST | `/api/principal_factors/list` | `kind`, `ref` | `principal_factors` |
| POST | `/api/principal_factors/create` | `kind`, `ref`, `attrs` | `principal_factor` |
| POST | `/api/principal_factors/update` | `id`, `attrs` | `principal_factor` |
| POST | `/api/principal_factors/delete` | `id` | `principal_factor` |

## Bindings

| Method | Path | Body | Response |
|---|---|---|---|
| POST | `/api/bindings/get` | `id` | `binding` |
| POST | `/api/bindings/list` | `principal_id` | `bindings` |
| POST | `/api/bindings/create` | `principal_id`, `attrs` | `binding` |
| POST | `/api/bindings/update` | `id`, `attrs` | `binding` |
| POST | `/api/bindings/delete` | `id` | `binding` |

## Fallback recovery

Fallback operations are absent from the compiled HTTP router. This includes public-key registry, envelope metadata, and private-key operations. Use trusted local Elixir calls or a controlled remote shell on the KMS host. See [Fallback recovery](fallback-recovery.md).

## Sessions

| Method | Path | Body | Response |
|---|---|---|---|
| POST | `/api/sessions/get` | `secret` | `session` |
| POST | `/api/sessions/active` | `secret` | `session` |
| POST | `/api/sessions/list` | `kind`, `ref` | `sessions` |
| POST | `/api/sessions/create` | `kind`, `ref`, `attrs`, `opts` | `session` |
| POST | `/api/sessions/passphrase` | `attrs` | `session` |
| POST | `/api/sessions/advance` | `secret`, `attrs` | `session` |
| POST | `/api/sessions/nudge` | `secret`, `opts` | `session` |
| POST | `/api/sessions/delete` | `secret` | `session` |
| POST | `/api/sessions/delete_all` | `kind`, `ref`, `opts` | `sessions` |
| POST | `/api/sessions/refresh` | `secret`, `opts` | `session` |

Session response JSON redacts bearer secrets in inspect-like contexts, but HTTP session creation returns the session secret because callers need it. Treat it like a password.

## WebAuthn

| Method | Path | Body | Response |
|---|---|---|---|
| POST | `/api/webauthn/registration/begin` | `kind`, `ref`, `opts` | challenge |
| POST | `/api/webauthn/registration/finish` | `challenge_id`, `response`, `opts` | credential |
| POST | `/api/webauthn/authentication/begin` | `kind`, `ref`, `opts` | challenge |
| POST | `/api/webauthn/authentication/finish` | `challenge_id`, `response`, `opts` | session |

See [WebAuthn](factors/webauthn.md).

## Security rules

- Use TLS or trusted private networking.
- Keep API tokens high entropy.
- Do not log request or response bodies.
- Do not log bearer tokens, plaintext, ciphertext payloads, passwords, factor codes, or RMK material.
- Put authorization in your application and/or use KMS sessions/bindings.
