# HawkTalk > OpenAI-compatible REST + Realtime voice AI API. Cloud-served on our own accelerator > fleet, with the same protocol available on-device. Three rails share one gateway, one model registry, > and one API-key system: REST (synchronous), WS (full-duplex realtime voice), and > HawkTalkLive (lane-muxed reactive conductor, preview). Adoption order is REST -> WS -> Live. Moving up is additive: models, keys and tool definitions carry over unchanged. ## Invariants Unknown values are `null` or the string `"unknown"`, never a fabricated 0. Unwired seams return 501 (`stt_not_wired` / `tts_not_wired`) so clients fall back to browser Web Speech — never synthetic audio or an invented transcript. Backend faults surface as explicit errors, never a silent truncation. End-of-speech is the client's VAD via `input_audio_buffer.commit`; the server adds semantic endpointing only and never emits `speech_started` / `speech_stopped`. Binary WebSocket frames are rejected by design. `response.cancel` rolls conversation history back to the turn boundary. ## Endpoints - REST base: `http://HOST:8890` — default bind is loopback `127.0.0.1` - WS base: `ws://HOST:8891` - Auth: `Authorization: Bearer sk-...`; WS also accepts `?api_key=...` or the subprotocol `openai-insecure-api-key.` - A node with no keys configured runs OPEN (anonymous); `GET /health` reports which. - [POST /v1/chat/completions](https://hawktalk.ai/docs#chat): OpenAI chat shape; `stream:true` gives SSE `chat.completion.chunk` then `data: [DONE]`. Response adds `x_timing` and `x_compute`. - [POST /v1/audio/transcriptions](https://hawktalk.ai/docs#stt): local whisper.cpp; multipart `file`, JSON base64, or raw `audio/*`. Returns `{text, x_stt}`. - [POST /v1/audio/speech](https://hawktalk.ai/docs#tts): local Kokoro ONNX; returns real `audio/wav` bytes. - [GET /v1/models](https://hawktalk.ai/docs#models): registry with availability and routing info. - [GET·POST /v1/compute-mode](https://hawktalk.ai/docs#compute): read or flip `casual` (NPU only) vs `pro` (NPU+CPU). Applies to the next turn. - [GET /health](https://hawktalk.ai/docs#auth): no auth; auth state, models, voice seams, telemetry, compute, memory. - [WS /v1/realtime](https://hawktalk.ai/docs#ws): alias of `/live/ouroboros`. OpenAI-Realtime event names and ordering. JSON text frames; audio is base64 pcm16, input default 24000 Hz, output frames carry `x_sample_rate_hz`. - [WS /live/brain](https://hawktalk.ai/docs#live): HawkTalkLive lane mux on `127.0.0.1:18898`. Preview, loopback-only, no auth wired. ## WebSocket events Send: `session.update`, `conversation.item.create`, `response.create`, `response.cancel`, `input_audio_buffer.append`, `input_audio_buffer.commit`. Receive: `session.created`, `session.updated`, `conversation.created`, `conversation.item.created`, `conversation.item.input_audio_transcription.completed`, `response.created`, `response.output_item.added`, `response.content_part.added`, `response.text.delta`, `response.audio.delta`, `response.viseme.delta`, `response.text.done`, `response.audio.done`, `response.content_part.done`, `response.output_item.done`, `response.done`, `rate_limits.updated`, `error`. HawkTalk-specific: `ouroboros.telemetry` (every 8 tokens + 5s idle heartbeat), `ouroboros.endpoint` / `ouroboros.endpoint.check` (semantic endpointing), `ouroboros.user_affect`, `ouroboros.presence`, `hawk.conduct`, `hawk.brain.hello`. `response.done` carries `x_ouroboros` with the turn's real measurements: `stt_ms`, `ttft_ms`, `first_audio_out_ms`, `end_to_end_ms`, `end_to_end_audio_ms`. ## Cost Rules an integrating agent should apply by default. These are decisions, not advice — each one changes the bill without changing what the product does. 1. Send `model: "auto"` unless the caller has a reason not to. The router picks a tier per utterance and answers trivial turns itself. Hardcoding the largest model is the single largest source of overspend on this API. 2. Match the rail to the interaction. REST (`/v1/chat/completions`) is a fraction of the cost of a realtime socket. Only open WS when the product needs interruption or streamed turn-taking. Dictation, ordering, form-filling and note capture do not. 3. Declare tools once with `session.update`, not on every `response.create`. Re-sending tool schemas per turn is billed per turn and buys nothing. 4. Keep the system prefix byte-identical across users so it can be cache-shared. Per-user memory belongs in retrieval, not in the prefix. 5. Mark non-interactive work `priority: "batch"`. Interruptible jobs run on interruptible capacity and are priced accordingly. Transcription of stored audio is always batch. 6. Never place a reasoning model in the voice path. Deliberation belongs on a second call or the async lane; the voice rail stays on the fast tier. 7. Retrieve rather than stuff. `/v1/embeddings` plus a retrieved prefix costs less than a long context and measures better. 8. Resize mid-call with `session.update` rather than provisioning the whole session at the highest tier it will ever need. Model changes take effect on the next turn without dropping the socket. 9. Read `x_timing` and `x_compute` before optimising. They report which tier answered and what each stage cost. Optimising without them is guessing. On the socket the equivalent is `x_ouroboros` on `response.done`. Machine-readable integration file for dropping into a customer repo: [AGENTS.md](https://hawktalk.ai/AGENTS.md). MCP server: `hawktalk-mcp`, tools `hawktalk_models`, `hawktalk_estimate`, `hawktalk_spend`, `hawktalk_cost_rules`, `hawktalk_test_turn`. ## Errors 400 `invalid_request` · 401 `invalid_api_key` · 404 `model_not_found` · 413 audio too large · 415 unsupported content-type · 429 `rate_limit_exceeded` (carries `Retry-After`) · 501 `stt_not_wired` / `tts_not_wired` · 502 `backend_failed` / `stt_failed` / `tts_failed` · 503 `model_unavailable` / `backend_unavailable` / `compute_not_wired`. Shape: `{"error": {"message", "type", "code", "param"}}`. ## Status Shipped: REST chat/models/health, WS Realtime session, per-key RPM limiting, usage ledger. Seam: REST STT and TTS return 501 on nodes where the seam is not wired. Preview: HawkTalkLive lane mux (loopback only, no auth). Roadmap: monthly token quota enforcement in the request path; public `api.hawktalk.ai`. ## Docs - [Developer docs](https://hawktalk.ai/docs): start here — rails, keys, hello world - [API primer](https://hawktalk.ai/apiprimer): auth, models, rate limits, errors - [REST tier](https://hawktalk.ai/rest) - [WS tier](https://hawktalk.ai/ws) - [HawkTalkLive tier](https://hawktalk.ai/live) - [Blade Frontier whitepaper](https://hawktalk.ai/whitepaper): the architecture thesis