Skip to content

Transcripts Guide

This guide explains how to access and work with conversation transcripts in the Vowel client library.

Table of Contents


Overview

The Vowel client automatically captures and stores conversation transcripts during voice sessions. Transcripts include both user speech and AI assistant responses, making them useful for:

  • Displaying conversation history in your UI
  • Persisting conversations across sessions
  • Analyzing conversation patterns
  • Building chat interfaces
  • Debugging voice interactions

Accessing Transcripts

There are several ways to access transcripts from the Vowel client:

1. Via getState() Method

Get the current state snapshot, including all transcripts:

typescript
import { Vowel } from '@vowel.to/client';

const vowel = new Vowel({
  appId: 'your-app-id',
  // ... other config
});

// Get current state
const state = vowel.getState();
console.log(state.transcripts); // VowelTranscript[]

// Access individual transcripts
state.transcripts.forEach((transcript, index) => {
  console.log(`${index + 1}. [${transcript.role}] ${transcript.text}`);
  console.log(`   Timestamp: ${transcript.timestamp}`);
});

2. Via onStateChange() Callback

Subscribe to state changes and access transcripts reactively:

typescript
// Subscribe to state changes
const unsubscribe = vowel.onStateChange((state) => {
  // Access transcripts from the state object
  const transcripts = state.transcripts;
  
  // React to new transcripts
  if (transcripts.length > 0) {
    const latestTranscript = transcripts[transcripts.length - 1];
    console.log(`New ${latestTranscript.role} message: ${latestTranscript.text}`);
  }
});

// Don't forget to unsubscribe when done
// unsubscribe();

3. Via React Hook useVowel()

Access transcripts in React components:

typescript
import { useVowel } from '@vowel.to/client/react';

function TranscriptPanel() {
  const { state } = useVowel();
  
  return (
    <div>
      <h2>Conversation History</h2>
      {state.transcripts.length === 0 ? (
        <p>No transcripts yet</p>
      ) : (
        <ul>
          {state.transcripts.map((transcript, index) => (
            <li key={index}>
              <strong>{transcript.role === 'user' ? 'You' : 'Assistant'}:</strong>
              {' '}
              {transcript.text}
              <br />
              <small>{transcript.timestamp.toLocaleTimeString()}</small>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

4. Via exportState() Method

Export transcripts along with other state for persistence:

typescript
// Export state including transcripts
const exportedState = vowel.exportState({ maxTurns: 20 });
console.log(exportedState.transcripts); // VowelTranscript[]

// Save to localStorage
localStorage.setItem('vowel-conversation', JSON.stringify(exportedState));

// Later, restore from localStorage
const saved = JSON.parse(localStorage.getItem('vowel-conversation') || '{}');
if (saved.transcripts) {
  console.log(`Restoring ${saved.transcripts.length} transcript(s)`);
  await vowel.startSession({ restoreState: saved });
}

5. Via onTranscriptEvent() Method (Internal)

⚠️ Note: This method is marked as @internal and is intended for internal use (caption system). It provides streaming transcript events but is not part of the public API.

typescript
// Internal API - use with caution
const unsubscribe = vowel.onTranscriptEvent((event) => {
  // event.type: 'delta' | 'done'
  // event.text: string
  // event.role: 'user' | 'assistant'
  // event.responseId?: string
  // event.itemId?: string
  
  if (event.type === 'delta') {
    console.log(`Streaming ${event.role} transcript: ${event.text}`);
  } else if (event.type === 'done') {
    console.log(`Complete ${event.role} transcript: ${event.text}`);
  }
});

Transcript Type Definition

The VowelTranscript interface is exported from the package:

typescript
import type { VowelTranscript } from '@vowel.to/client';

interface VowelTranscript {
  role: "user" | "assistant";
  text: string;
  timestamp: Date;
}

Fields:

  • role: "user" for user speech, "assistant" for AI responses
  • text: The transcribed text content
  • timestamp: Date object indicating when the transcript was created

Common Use Cases

Displaying Conversation History

typescript
import { useVowel } from '@vowel.to/client/react';

function ConversationHistory() {
  const { state } = useVowel();
  
  return (
    <div className="conversation-history">
      {state.transcripts.map((transcript, index) => (
        <div 
          key={index} 
          className={`message ${transcript.role}`}
        >
          <div className="role">{transcript.role === 'user' ? 'You' : 'AI'}</div>
          <div className="text">{transcript.text}</div>
          <div className="timestamp">
            {transcript.timestamp.toLocaleTimeString()}
          </div>
        </div>
      ))}
    </div>
  );
}

Persisting Conversations

typescript
// Save transcripts periodically
vowel.onStateChange((state) => {
  if (state.transcripts.length > 0) {
    const exported = vowel.exportState({ maxTurns: 50 });
    
    // Save to IndexedDB for persistence
    saveToIndexedDB('conversations', exported);
    
    // Or save to localStorage
    localStorage.setItem('vowel-conversation', JSON.stringify(exported));
  }
});

// Restore on page load
window.addEventListener('load', async () => {
  const saved = localStorage.getItem('vowel-conversation');
  if (saved) {
    const state = JSON.parse(saved);
    if (state.transcripts && state.transcripts.length > 0) {
      await vowel.startSession({ restoreState: state });
    }
  }
});

Filtering Transcripts

typescript
// Get only user transcripts
const userTranscripts = state.transcripts.filter(t => t.role === 'user');

// Get only assistant transcripts
const assistantTranscripts = state.transcripts.filter(t => t.role === 'assistant');

// Get transcripts from last 5 minutes
const recentTranscripts = state.transcripts.filter(t => {
  const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
  return t.timestamp > fiveMinutesAgo;
});

Clearing Transcripts

typescript
// Clear all transcripts
vowel.clearTranscripts();

// Or via React hook
const { clearTranscripts } = useVowel();
clearTranscripts();

Exporting Transcripts for Analysis

typescript
// Export transcripts as JSON
const transcripts = vowel.getState().transcripts;
const json = JSON.stringify(transcripts, null, 2);

// Export as CSV
const csv = transcripts.map(t => 
  `${t.timestamp.toISOString()},${t.role},"${t.text.replace(/"/g, '""')}"`
).join('\n');

// Export as plain text
const text = transcripts.map(t => 
  `${t.role === 'user' ? 'You' : 'Assistant'}: ${t.text}`
).join('\n\n');

Building a Chat Interface

typescript
import { useVowel } from '@vowel.to/client/react';
import { useState, useEffect } from 'react';

function ChatInterface() {
  const { state } = useVowel();
  const [messages, setMessages] = useState(state.transcripts);
  
  useEffect(() => {
    setMessages(state.transcripts);
  }, [state.transcripts]);
  
  return (
    <div className="chat-container">
      <div className="messages">
        {messages.map((transcript, index) => (
          <div 
            key={index}
            className={`message ${transcript.role}`}
          >
            <div className="avatar">
              {transcript.role === 'user' ? '👤' : '🤖'}
            </div>
            <div className="content">
              <div className="text">{transcript.text}</div>
              <div className="time">
                {transcript.timestamp.toLocaleTimeString()}
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

API Reference

Methods

getState(): VoiceSessionState

Returns the current state snapshot including transcripts.

typescript
const state = vowel.getState();
const transcripts = state.transcripts; // VowelTranscript[]

onStateChange(listener: (state: VoiceSessionState) => void): () => void

Subscribe to state changes. Returns an unsubscribe function.

typescript
const unsubscribe = vowel.onStateChange((state) => {
  console.log(state.transcripts);
});

exportState(options?: { maxTurns?: number }): VoiceSessionState

Export state including transcripts. Optionally limit to last N turns.

typescript
const state = vowel.exportState({ maxTurns: 20 });
const transcripts = state.transcripts;

clearTranscripts(): void

Clear all transcripts from the current state.

typescript
vowel.clearTranscripts();

React Hook

useVowel(): VowelContextType

React hook that provides access to state including transcripts.

typescript
const { state, clearTranscripts } = useVowel();
const transcripts = state.transcripts;

Types

VowelTranscript

typescript
interface VowelTranscript {
  role: "user" | "assistant";
  text: string;
  timestamp: Date;
}

VoiceSessionState

typescript
interface VoiceSessionState {
  // ... other state fields
  transcripts: VowelTranscript[];
  // ... other state fields
}

Best Practices

  1. Limit transcript history: Use exportState({ maxTurns: N }) to avoid storing too many transcripts
  2. Persist selectively: Only save transcripts that are meaningful to your use case
  3. Handle timestamps: Remember that timestamp is a Date object - serialize/deserialize appropriately when persisting
  4. Clear old transcripts: Periodically clear transcripts to prevent memory bloat in long-running sessions
  5. Use state callbacks: Prefer onStateChange() over polling getState() for reactive updates


Last Updated: February 6, 2026