[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"article-doc:docs\u002Fguides\u002F5.save-transcript":3},"---\ntitle: Save a Transcript\ndescription: Persist the complete transcript after a session ends.\n---\nHow to collect and persist a complete transcript after a realtime session ends.\n\n## Collecting segments\n\nDuring the session, accumulate `conversation.item.input_audio_transcription.completed` events:\n\n```typescript\nconst segments = [];\n\nws.onmessage = (event) => {\n  const data = JSON.parse(event.data);\n  if (data.type === 'conversation.item.input_audio_transcription.completed') {\n    segments.push({\n      utterance_index: data.utterance_index,\n      text: data.text,\n      language: data.language,\n      audio_duration_ms: data.audio_duration_ms,\n      latency_ms: data.latency_ms,\n      timestamp: Date.now(),\n    });\n  }\n};\n\nws.onclose = () => {\n  \u002F\u002F Session ended — save the complete transcript\n  saveTranscript(segments);\n};\n```\n\n## Merging into a full transcript\n\n```typescript\nfunction mergeTranscript(segments) {\n  \u002F\u002F Sort by utterance_index to ensure order\n  segments.sort((a, b) => a.utterance_index - b.utterance_index);\n  return segments.map(s => s.text).join('\\n');\n}\n```\n\n## Storage format\n\nRecommended JSON structure:\n\n```json\n{\n  \"session_id\": \"sess_...\",\n  \"started_at\": \"2026-08-15T10:00:00Z\",\n  \"ended_at\": \"2026-08-15T10:30:00Z\",\n  \"language\": \"zh\",\n  \"segments\": [\n    {\n      \"utterance_index\": 0,\n      \"text\": \"The weather is nice today\",\n      \"audio_duration_ms\": 3200,\n      \"latency_ms\": 480\n    }\n  ],\n  \"full_text\": \"The weather is nice today\\nIt might rain tomorrow\"\n}\n```\n\n## Detecting missing segments\n\nCheck for gaps in `utterance_index`:\n\n```typescript\nfunction findMissing(segments) {\n  const indices = segments.map(s => s.utterance_index);\n  const max = Math.max(...indices);\n  const missing = [];\n  for (let i = 0; i \u003C= max; i++) {\n    if (!indices.includes(i)) missing.push(i);\n  }\n  return missing;\n}\n```\n\nMissing segments may occur if frames were dropped due to backpressure or concurrent utterance limits.\n\n## Offline alternative\n\nFor cases where you need a guaranteed complete transcript, consider using the [Recorded API](\u002Fdocs\u002Frecorded\u002Ftranscribe-audio) instead — submit the recorded audio file and get the full structured result.\n\n## Related\n\n- [Transcript Lifecycle](\u002Fdocs\u002Frealtime\u002Ftranscript-lifecycle) — event states\n- [Transcribe Audio](\u002Fdocs\u002Frecorded\u002Ftranscribe-audio) — offline alternative\n- [Subtitles](\u002Fdocs\u002Frecorded\u002Fsubtitles) — subtitle generation\n",1790059118952]