Save a Transcript

Persist the complete transcript after a session ends.

How to collect and persist a complete transcript after a realtime session ends.

Collecting segments

During the session, accumulate conversation.item.input_audio_transcription.completed events:

const segments = [];

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data.type === 'conversation.item.input_audio_transcription.completed') {
    segments.push({
      utterance_index: data.utterance_index,
      text: data.text,
      language: data.language,
      audio_duration_ms: data.audio_duration_ms,
      latency_ms: data.latency_ms,
      timestamp: Date.now(),
    });
  }
};

ws.onclose = () => {
  // Session ended — save the complete transcript
  saveTranscript(segments);
};

Merging into a full transcript

function mergeTranscript(segments) {
  // Sort by utterance_index to ensure order
  segments.sort((a, b) => a.utterance_index - b.utterance_index);
  return segments.map(s => s.text).join('\n');
}

Storage format

Recommended JSON structure:

{
  "session_id": "sess_...",
  "started_at": "2026-08-15T10:00:00Z",
  "ended_at": "2026-08-15T10:30:00Z",
  "language": "zh",
  "segments": [
    {
      "utterance_index": 0,
      "text": "The weather is nice today",
      "audio_duration_ms": 3200,
      "latency_ms": 480
    }
  ],
  "full_text": "The weather is nice today\nIt might rain tomorrow"
}

Detecting missing segments

Check for gaps in utterance_index:

function findMissing(segments) {
  const indices = segments.map(s => s.utterance_index);
  const max = Math.max(...indices);
  const missing = [];
  for (let i = 0; i <= max; i++) {
    if (!indices.includes(i)) missing.push(i);
  }
  return missing;
}

Missing segments may occur if frames were dropped due to backpressure or concurrent utterance limits.

Offline alternative

For cases where you need a guaranteed complete transcript, consider using the Recorded API instead — submit the recorded audio file and get the full structured result.