{
  "title": "Streaming Speech to Text",
  "endpoint": "wss://api.x.ai/v1/stt",
  "description": "Real-time streaming speech-to-text via WebSocket. Stream raw audio as binary frames and receive JSON transcript events as the audio is processed. Configuration is done via query parameters at connection time.",

  "authentication": {
    "headers": [
      {
        "name": "Authorization",
        "type": "string",
        "required": true,
        "description": "Bearer token authentication. Format: `Bearer <your xAI API key>`.",
        "example": "Bearer $XAI_API_KEY"
      }
    ]
  },

  "queryParameters": [
    {
      "name": "sample_rate",
      "type": "integer",
      "required": false,
      "default": 16000,
      "description": "Audio sample rate in Hz. Supported values: `8000`, `16000`, `22050`, `24000`, `44100`, `48000`."
    },
    {
      "name": "encoding",
      "type": "string",
      "required": false,
      "default": "pcm",
      "description": "Audio encoding format. `pcm` — signed 16-bit little-endian (2 bytes/sample). `mulaw` — G.711 µ-law (1 byte/sample). `alaw` — G.711 A-law (1 byte/sample)."
    },
    {
      "name": "interim_results",
      "type": "boolean",
      "required": false,
      "default": false,
      "description": "When `true`, the server emits partial transcript events (`is_final=false`) approximately every 500 ms while audio is being processed. When `false` (default), only finalized results are sent."
    },
    {
      "name": "endpointing",
      "type": "integer",
      "required": false,
      "default": 10,
      "description": "Silence duration in milliseconds before the server fires a `speech_final=true` event, indicating the speaker stopped talking. Range: 0–5000. Set to `0` for no delay (fire on any VAD silence boundary). Default: 10ms."
    },
    {
      "name": "language",
      "type": "string",
      "required": false,
      "default": "",
      "description": "Language code (e.g. `en`, `fr`, `de`, `ja`). When set, enables Inverse Text Normalization — spoken-form numbers, currencies, and units are converted to their written form."
    },
    {
      "name": "multichannel",
      "type": "boolean",
      "required": false,
      "default": false,
      "description": "When `true`, enables per-channel transcription for interleaved multichannel audio. Requires `channels` to be set to ≥ 2."
    },
    {
      "name": "channels",
      "type": "integer",
      "required": false,
      "default": 1,
      "description": "Number of interleaved audio channels. Required when `multichannel=true`. Min: 2, Max: 8."
    },
    {
      "name": "diarize",
      "type": "boolean",
      "required": false,
      "default": false,
      "description": "When `true`, enables speaker diarization. Words in `transcript.partial` and `transcript.done` events include a `speaker` field (integer) identifying the detected speaker."
    },
    {
      "name": "keyterm",
      "type": "string (repeatable)",
      "required": false,
      "description": "A key term to bias transcription toward (e.g. product names, proper nouns). Repeat the parameter for each term (e.g. `keyterm=Understand+The+Universe`). Max 100 terms, each up to 50 characters."
    },
    {
      "name": "filler_words",
      "type": "boolean",
      "required": false,
      "default": false,
      "description": "When `true`, filler words (e.g. `uh`, `um`, `er`) are included in the transcript. When `false` (default), filler words are automatically removed from the transcript text and the `words` array."
    },
    {
      "name": "smart_turn",
      "type": "number",
      "required": false,
      "description": "Enable Smart Turn end-of-turn detection. Set to a confidence threshold between `0.0` and `1.0`. When the model's end-of-turn probability exceeds this threshold at a VAD silence boundary, `speech_final` fires immediately. When confidence is below the threshold, `speech_final` is suppressed and the event is demoted to `chunk_final`. Every `transcript.partial` event includes an `end_of_turn_confidence` field (0.0–1.0) when Smart Turn is enabled. Example: `smart_turn=0.7`."
    },
    {
      "name": "smart_turn_timeout",
      "type": "integer",
      "required": false,
      "description": "Maximum silence duration in milliseconds before forcing `speech_final`, even when the Smart Turn model predicts the speaker hasn't finished. Acts as a safety net to prevent sessions from hanging during extended silence. Only applies when `smart_turn` is enabled. Range: 1–5000. Example: `smart_turn_timeout=3000`."
    },
    {
      "name": "vad_threshold",
      "type": "number",
      "required": false,
      "default": 0.08,
      "description": "Speech-probability threshold for the voice-activity gate (0.0–1.0). Audio in chunks scoring below the threshold is treated as non-speech and skipped for transcription. Lower values transcribe quieter or noisier speech (e.g. narrowband telephony) but may produce spurious text for background noise; `0` disables the gate entirely. Does not affect endpointing or `speech_final` timing. Default: `0.08`."
    }
  ],

  "clientMessages": [
    {
      "type": "Binary frame (audio)",
      "description": "Send raw audio as binary WebSocket frames in the encoding specified by the `encoding` query parameter. Audio should be streamed in real-time-paced chunks (e.g. 100 ms at a time). No base64 encoding — send raw bytes directly.",
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "Raw audio bytes in the specified encoding (pcm, mulaw, or alaw)."
      },
      "example": "(raw binary audio data)"
    },
    {
      "type": "finalize",
      "description": "Force the current utterance to finalize as `speech_final` immediately, without waiting for VAD endpointing or Smart Turn. The session stays open so you can continue streaming audio. Accepts `finalize` or `Finalize` as the type value. When `multichannel=true`, optional `channel` (0-based) limits the finalize to that channel; omit `channel` to finalize every channel.",
      "schema": {
        "type": "object",
        "required": ["type"],
        "properties": {
          "type": {
            "type": "string",
            "enum": ["finalize", "Finalize"],
            "description": "Must be `finalize` or `Finalize`."
          },
          "channel": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional 0-based channel index. Only meaningful when `multichannel=true`. When omitted, all channels are finalized."
          }
        }
      },
      "example": {
        "type": "Finalize",
        "channel": 0
      }
    },
    {
      "type": "audio.done",
      "description": "Signal that all audio has been sent. The server flushes any remaining buffered audio, emits final transcript events, and sends a `transcript.done` event. The connection closes after `transcript.done`.",
      "schema": {
        "type": "object",
        "required": ["type"],
        "properties": {
          "type": {
            "type": "string",
            "const": "audio.done",
            "description": "Must be `audio.done`."
          }
        }
      },
      "example": {
        "type": "audio.done"
      }
    }
  ],

  "serverMessages": [
    {
      "type": "transcript.created",
      "description": "Sent immediately after the WebSocket connection is established and the server is ready to receive audio. **Wait for this event before sending audio** — the server needs to initialize its ASR backend.",
      "schema": {
        "type": "object",
        "required": ["type", "id"],
        "properties": {
          "type": {
            "type": "string",
            "const": "transcript.created",
            "description": "Always `transcript.created`."
          },
          "id": {
            "type": "string",
            "description": "Unique session identifier (UUID)."
          }
        }
      },
      "example": {
        "type": "transcript.created",
        "id": "83f2f6fd-1cd1-4747-bc52-cebddc961c32"
      }
    },
    {
      "type": "transcript.partial",
      "description": "A transcript result for a portion of the audio stream. Two boolean fields convey state: interim (`is_final=false`) means text may still change, chunk final (`is_final=true`, `speech_final=false`) means the chunk is locked, and utterance final (`is_final=true`, `speech_final=true`) means the speaker stopped talking.",
      "schema": {
        "type": "object",
        "required": ["type", "text", "words", "is_final", "speech_final", "start", "duration"],
        "properties": {
          "type": {
            "type": "string",
            "const": "transcript.partial",
            "description": "Always `transcript.partial`."
          },
          "text": {
            "type": "string",
            "description": "Transcript text for this chunk."
          },
          "words": {
            "type": "array",
            "description": "Word-level details with timestamps and confidence scores.",
            "items": {
              "type": "object",
              "properties": {
                "text": { "type": "string", "description": "The word text." },
                "start": {
                  "type": "number",
                  "description": "Word start time in seconds (2 d.p.)."
                },
                "end": { "type": "number", "description": "Word end time in seconds (2 d.p.)." },
                "confidence": {
                  "type": "number",
                  "description": "Confidence score (0.0–1.0). Omitted when 0."
                },
                "speaker": {
                  "type": "integer",
                  "description": "Speaker index (0-based). Only present when `diarize=true`."
                }
              }
            }
          },
          "is_final": {
            "type": "boolean",
            "description": "Chunk-level finality. `false` = partial (text may change). `true` = chunk fully transcribed (text locked)."
          },
          "speech_final": {
            "type": "boolean",
            "description": "Utterance-level finality. `true` = speaker stopped talking (VAD endpointing). Only meaningful when `is_final=true`."
          },
          "start": {
            "type": "number",
            "description": "Start position in the audio stream (seconds from stream start, 2 d.p.)."
          },
          "duration": {
            "type": "number",
            "description": "Duration of audio covered by this result (seconds, 2 d.p.)."
          },
          "channel_index": {
            "type": "integer",
            "description": "Channel index. Only present when `multichannel=true`."
          },
          "end_of_turn_confidence": {
            "type": "number",
            "description": "End-of-turn confidence from the Smart Turn model (0.0–1.0). Only present when `smart_turn` is enabled. Higher values indicate the speaker has likely finished their thought. During active speech the value is `0.0`; at silence boundaries the model evaluates accumulated audio and publishes a confidence score."
          }
        }
      },
      "example": {
        "type": "transcript.partial",
        "text": "The balance is $167,983.15.",
        "words": [
          { "text": "The", "start": 0.24, "end": 0.48, "confidence": 0.95 },
          { "text": "balance", "start": 0.48, "end": 0.96, "confidence": 0.92 },
          { "text": "is", "start": 0.96, "end": 1.12, "confidence": 0.98 },
          { "text": "$167,983.15.", "start": 1.12, "end": 3.2, "confidence": 0.89 }
        ],
        "is_final": true,
        "speech_final": false,
        "start": 0.0,
        "duration": 3.2
      }
    },
    {
      "type": "transcript.done",
      "description": "Final transcript after `audio.done`. `duration` always present. One per channel when `multichannel=true`. Connection closes after this event.",
      "schema": {
        "type": "object",
        "required": ["type", "text", "words", "duration"],
        "properties": {
          "type": {
            "type": "string",
            "const": "transcript.done",
            "description": "Always `transcript.done`."
          },
          "text": {
            "type": "string",
            "description": "Final transcript text."
          },
          "words": {
            "type": "array",
            "description": "Word-level details for the final transcript.",
            "items": {
              "type": "object",
              "properties": {
                "text": { "type": "string" },
                "start": { "type": "number" },
                "end": { "type": "number" },
                "confidence": { "type": "number" },
                "speaker": {
                  "type": "integer",
                  "description": "Speaker index (0-based). Only present when `diarize=true`."
                }
              }
            }
          },
          "duration": {
            "type": "number",
            "description": "Total audio duration processed (seconds, 2 d.p.)."
          },
          "channel_index": {
            "type": "integer",
            "description": "Channel index. Only present when `multichannel=true`."
          }
        }
      },
      "example": {
        "type": "transcript.done",
        "text": "",
        "words": [],
        "duration": 6.43
      }
    },
    {
      "type": "error",
      "description": "An error occurred during the session. Most errors (pipeline failures, stream timeouts) close the connection. Only client message parse errors keep the connection open.",
      "schema": {
        "type": "object",
        "required": ["type", "message"],
        "properties": {
          "type": {
            "type": "string",
            "const": "error",
            "description": "Always `error`."
          },
          "message": {
            "type": "string",
            "description": "Human-readable error description."
          }
        }
      },
      "example": {
        "type": "error",
        "message": "Invalid message: expected {\"type\": \"audio.done\"}"
      }
    }
  ],

  "exampleFlow": [
    { "direction": "server", "type": "transcript.created" },
    { "direction": "client", "type": "Binary frame (audio)" },
    { "direction": "client", "type": "Binary frame (audio)" },
    { "direction": "server", "type": "transcript.partial", "label": "interim (is_final=false)" },
    { "direction": "client", "type": "Binary frame (audio)" },
    { "direction": "server", "type": "transcript.partial", "label": "chunk final (is_final=true)" },
    { "direction": "client", "type": "Binary frame (audio)" },
    {
      "direction": "server",
      "type": "transcript.partial",
      "label": "utterance final (speech_final=true)"
    },
    { "direction": "client", "type": "audio.done" },
    { "direction": "server", "type": "transcript.done" }
  ]
}
