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:
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:
// 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:
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:
// 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.
// 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:
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 responsestext: The transcribed text contenttimestamp:Dateobject indicating when the transcript was created
Common Use Cases
Displaying Conversation History
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
// 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
// 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
// Clear all transcripts
vowel.clearTranscripts();
// Or via React hook
const { clearTranscripts } = useVowel();
clearTranscripts();Exporting Transcripts for Analysis
// 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
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.
const state = vowel.getState();
const transcripts = state.transcripts; // VowelTranscript[]onStateChange(listener: (state: VoiceSessionState) => void): () => void
Subscribe to state changes. Returns an unsubscribe function.
const unsubscribe = vowel.onStateChange((state) => {
console.log(state.transcripts);
});exportState(options?: { maxTurns?: number }): VoiceSessionState
Export state including transcripts. Optionally limit to last N turns.
const state = vowel.exportState({ maxTurns: 20 });
const transcripts = state.transcripts;clearTranscripts(): void
Clear all transcripts from the current state.
vowel.clearTranscripts();React Hook
useVowel(): VowelContextType
React hook that provides access to state including transcripts.
const { state, clearTranscripts } = useVowel();
const transcripts = state.transcripts;Types
VowelTranscript
interface VowelTranscript {
role: "user" | "assistant";
text: string;
timestamp: Date;
}VoiceSessionState
interface VoiceSessionState {
// ... other state fields
transcripts: VowelTranscript[];
// ... other state fields
}Best Practices
- Limit transcript history: Use
exportState({ maxTurns: N })to avoid storing too many transcripts - Persist selectively: Only save transcripts that are meaningful to your use case
- Handle timestamps: Remember that
timestampis aDateobject - serialize/deserialize appropriately when persisting - Clear old transcripts: Periodically clear transcripts to prevent memory bloat in long-running sessions
- Use state callbacks: Prefer
onStateChange()over pollinggetState()for reactive updates
Related Documentation
- Quick Reference - Quick reference for common operations
- Pause/Resume and State Restoration - State persistence guide
Last Updated: February 6, 2026