# Device key vending — `/api/device/keys`

The runtime's zero-key-setup contract for paid capabilities. The smart model
already routes through the platform proxy (`/api/ai/anthropic`) with the
device credential; hearing (Soniox), voice (ElevenLabs), and the fast lane
(Groq) instead call their vendors **directly from the device** and are billed
via `/api/device/billing/usage` on the capability rate card. This endpoint is
the missing piece: it hands the device the vendor keys so no user ever edits
`~/.skykoi/.env`.

The device side is already implemented (`src/infra/provisioned-keys.ts`) and
live in every install: it calls this endpoint at capture start and on a 12h
TTL, caches to `~/.skykoi/credentials/provisioned-keys.json` (0600), and every
key resolver falls back to it after env/.env. Until the endpoint is deployed
it 404s once per boot and installs behave exactly as before.

## Contract

```
GET /api/device/keys
x-api-key: <koiId>.<gatewayToken>     ← same composite credential as billing
```

**200**
```json
{
  "keys": {
    "soniox":     { "apiKey": "…", "expiresAt": 1784260000000 },
    "groq":       { "apiKey": "…" },
    "elevenlabs": { "apiKey": "…" }
  }
}
```

- Provider names are lowercase; unknown providers are accepted and resolved
  by name (future capabilities need no client change).
- `expiresAt` (ms epoch, optional): the client refreshes 10 minutes before
  expiry and never uses an expired key.
- **404/501**: client disables itself until next gateway boot (deploy-safe).
- Auth failure should be 401/403 (client keeps its cache and retries on TTL).

## Server side (SHIPPED — skykoi-live `src/app/api/device/keys/route.ts`)

- **Soniox**: a short-lived key is minted per request via
  `POST https://api.soniox.com/v1/auth/temporary-api-key` with the org master
  key (`SONIOX_API_KEY`): `usage_type: "transcribe_websocket"`,
  `expires_in_seconds: 3600` (Soniox max), `max_session_duration_seconds:
  18000` so an established stream rides out key expiry, and
  `client_reference_id: koiId` for per-koi attribution. The master key never
  leaves the platform; a leaked device key dies within the hour. Because
  temp keys expire, refresh-capable clients re-resolve the key at every
  websocket CONNECT (windows-audio does); clients with a static environment
  (the macOS launchd daemon) must NOT be handed temp keys — ops sets
  `SONIOX_VEND_MODE=static` + `SONIOX_DEVICE_API_KEY` for those fleets until
  the daemon can re-fetch, and the runtime only bakes non-expiring keys into
  the launchd plist.
- **Groq / ElevenLabs**: no temporary-key facility upstream —
  `GROQ_DEVICE_API_KEY` / `ELEVENLABS_DEVICE_API_KEY` pass through (fall
  back to the org keys). Issue restricted per-fleet keys in the vendor
  consoles to rotate without a deploy. HTTPS proxies mirroring
  `/api/ai/anthropic` remain the safer long-term shape for these two.
- Auth is the shared `authorizeDeviceKoi` (timing-safe compare against
  `koi.gatewayToken`), plus a 10/min per-koi rate limit. Per-provider
  fail-soft: one vendor outage never blanks the response.
- TODO once `/api/device/billing/*` ships: gate vending on the org's credit
  balance / capability entitlements — same check as the usage writer.
