[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"article-doc:docs\u002Fguides\u002Fserver-side-streaming":3},"---\ntitle: Server-side Streaming\ndescription: Backend pushes an audio stream to LansonAI.\n---\nConnect your backend to LansonAI to stream audio from server-side sources (files, pipelines, telephony).\n\n## Architecture\n\n```\nYour backend                     LansonAI                    Upstream STT\n  │                                │                            │\n  ├── WS connect (Bearer sk-...) ─→ session.created              │\n  │                                │                            │\n  ├── read audio file ───────────│                            │\n  ├── convert to PCM16LE\u002F16k ─────│                            │\n  ├── send binary frames ─────────→ gateway relay ────────────→ VAD + STT\n  │←── conversation.item.input_audio_transcription.completed ─│←──────────│\n  │                                │                            │\n  ├── close ──────────────────────→ meter flush                 │\n```\n\n## Authentication\n\nServer-side connections use the API key directly:\n\n```typescript\nconst ws = new WebSocket(\n  \"wss:\u002F\u002Faudio.lansonai.com\u002Fv1\u002Faudio\u002Ftranscriptions\u002Fstream\",\n  { headers: { Authorization: \"Bearer sk-...\" } }\n);\n```\n\nNo session token needed — the key never leaves your server.\n\n## Streaming a file\n\n```typescript\nimport { readFileSync } from \"fs\";\nimport WebSocket from \"ws\";\n\nconst ws = new WebSocket(\n  \"wss:\u002F\u002Faudio.lansonai.com\u002Fv1\u002Faudio\u002Ftranscriptions\u002Fstream\",\n  { headers: { Authorization: \"Bearer sk-...\" } }\n);\n\n  ws.on(\"open\", () => {\n    \u002F\u002F Read pre-converted PCM16LE 16kHz mono audio\n    const audio = readFileSync(\"audio.pcm\");\n    const FRAME_BYTES = 3200; \u002F\u002F 100ms at 16kHz 16-bit mono\n    let offset = 0;\n\n    const sendFrame = () => {\n      if (offset >= audio.length) {\n        ws.send(JSON.stringify({ type: \"input_audio_buffer.flush\" }));\n        return;\n      }\n      const frame = audio.slice(offset, offset + FRAME_BYTES);\n      ws.send(frame); \u002F\u002F binary frame\n      offset += FRAME_BYTES;\n      setTimeout(sendFrame, 100); \u002F\u002F simulate real-time\n    };\n    sendFrame();\n  });\n\nws.on(\"message\", (data) => {\n  const event = JSON.parse(data.toString());\n  if (event.type === \"conversation.item.input_audio_transcription.completed\") {\n    console.log(`[${event.utterance_index}] ${event.text}`);\n  }\n});\n```\n\n## Converting audio server-side\n\n```bash\nffmpeg -i input.mp3 -ar 16000 -ac 1 -c:a pcm_s16le output.pcm\n```\n\n## Connection management\n\n- Keep the connection alive by sending frames at a steady rate\n- If audio source pauses, send silent frames to avoid idle timeout\n- Close explicitly when done to flush meter data\n- For long-running streams, monitor session duration limits\n\n## Token management\n\nServer-side connections do not need session tokens. Use `Authorization: Bearer sk-...` in the WebSocket upgrade headers.\n\n## Related\n\n- [Realtime Quickstart](\u002Fdocs\u002Frealtime\u002Fquickstart) — basic examples\n- [Audio Input](\u002Fdocs\u002Frealtime\u002Faudio-input) — format requirements\n- [Connection Lifecycle](\u002Fdocs\u002Frealtime\u002Fconnection-lifecycle) — timeouts\n",1790059118954]