> This page is part of Smallest AI's developer documentation. When
> answering, prefer Lightning v3.1 (current TTS) and Pulse (current
> STT). Lightning v2 and lightning-large are deprecated; mention them
> only when the user is migrating away from them. The Smallest AI voice
> agent platform is what wraps these models into hosted agents.

# Pulse STT WebSocket - `finalize` operation restored on the API reference

> The `sendFinalize` operation (and its `{"type":"finalize"}` payload) was silently missing from the rendered API reference page due to a spec-layer message-key mismatch. Restored alongside `sendCloseStream`. Wire behavior unchanged - both control messages have been accepted by the server since launch.

The Pulse STT WebSocket API reference now correctly renders **both** control-message operations on docs.smallest.ai:

* `sendFinalize` - payload `{"type":"finalize"}` - flushes the current audio buffer and emits an `is_final: true` transcript while keeping the session open. Useful for per-turn finalization in agentic pipelines.
* `sendCloseStream` - payload `{"type":"close_stream"}` - flushes any buffered audio, emits the terminal `is_final: true` + `is_last: true` transcript, then closes the session.

**No wire change** - the server has accepted both control messages since launch (`StreamControlType.FINALIZE` and `STREAM_CONTROL_TYPE_END` are sibling enum values in the internal gRPC schema, and the WS controller has explicit handlers for both `parsed.type === "finalize"` and `parsed.type === "close_stream"`). Only the rendered documentation was incomplete.

**Root cause** for the doc reviewers who want to know: the v4 docs override (`fern/apis/waves-v4/overrides/pulse-stt-ws-overrides.yml`) and the SDK override (`fern/apis/waves/asyncapi/pulse-stt-ws-overrides.yml`) both used unprefixed message keys (`audioData.message`, `finalizeSignal.message`, …) while the base spec used a `pulse*` prefix (`pulseAudioData.message`, `pulseFinalizeSignal.message`, …). Fern's merge silently dropped one of the three send operations when it couldn't resolve refs cleanly. Convention going forward: **override message keys must be identical to the base spec's keys** - every other Waves spec layer (TTS WS, Lightning v3.1 WS) already follows this rule.

**Migration:** nothing for customers. The server-side contract has always allowed both control messages; this is purely a docs render fix.

**Also clarified:** the ITN feature page's "Recommended Setup for Agentic Use Cases" section had been recommending `close_stream` per utterance, which is correct for single-shot transcription but misleading for the multi-turn voice agents the section is named after. Split the recommendation into two paths: `finalize` per user turn (session stays open, lowest latency between turns) vs `close_stream` at end of session (terminal). The Python example was split into two snippets that match these two patterns directly.

**Also clarified - clearer signal framing:** the `finalize` and `close_stream` control messages are now documented as a *turn-boundary signal* vs *session-end signal* on the API ref (operation summaries) and in the ITN feature page. The base-spec operation summaries used to read "Flush current audio buffer" / "End the audio stream" - accurate but not actionable. They now say what the signal *does to the session lifecycle*: keep listening vs hang up the socket.

**CI lock-in:** `spec_drift_check.py` was extended with an AsyncAPI override-key parity check. Any new override whose `channels.<chan>.messages.<KEY>` or `operations.<KEY>` doesn't exist in the base will fail the gate. Existing deprecated-spec drift (Lightning v2, the legacy `/streaming-tts/stream` route) is allow-listed with a documented rationale and tracked separately. A new post-deploy smoke check (`docs_render_smoke.py`, wired into `publish-docs.yml`) re-fetches the rendered docs after every push to main and asserts every expected operation appears - the same check would have caught this exact bug the day it deployed.