Overview
The live transcript functionality provides a WebSocket endpoint that streams real-time transcriptions of ongoing calls. This enables powerful use cases like:
Call Monitoring Dashboards : Build real-time interfaces for supervisors
Live Customer Service : Monitor calls as they happen for quality assurance
Real-time Analytics : Process transcripts for sentiment analysis and insights
Accessibility Features : Provide live captions for hearing-impaired users
Training & Coaching : Help agents improve through real-time feedback
WebSocket Endpoint
Connect to the live transcript stream using the following WebSocket URL:
ws://burki.dev/live-transcript/{call_sid}?token={api_key}
Path Parameters:
call_sid
(string, required ): The Twilio Call SID of the call you want to monitor
Authentication:
The WebSocket endpoint requires authentication using your API key. You can authenticate using any of these methods:
Query Parameter (Recommended): ?token=your_api_key
Authorization Header : Authorization: Bearer your_api_key
WebSocket Subprotocol : burki-token-your_api_key
Security Note : Only users from the same organization as the call’s assistant can access the transcript stream. API keys are validated and must have appropriate permissions.
Message Types
The WebSocket sends different types of messages to keep you informed about the call status and transcript updates.
Connection Established
Sent immediately when the WebSocket connection is successfully established.
{
"type" : "connection_established" ,
"call_sid" : "CA123abc..." ,
"timestamp" : "2024-01-01T12:00:00.000Z" ,
"message" : "Connected to live transcript stream"
}
Transcript Messages
The main message type containing real-time transcript segments from both the user and assistant.
{
"type" : "transcript" ,
"call_sid" : "CA123abc..." ,
"timestamp" : "2024-01-01T12:00:05.123Z" ,
"data" : {
"content" : "Hello, how can I help you today?" ,
"speaker" : "assistant" ,
"is_final" : true ,
"confidence" : 0.95 ,
"segment_start" : 5.2 ,
"segment_end" : 7.1 ,
"created_at" : "2024-01-01T12:00:05.123Z"
}
}
Transcript Data Fields:
content
: The transcribed text
speaker
: Either "user"
or "assistant"
is_final
: Whether this is a final transcript (usually true
)
confidence
: Confidence score from 0.0 to 1.0 (may be null
)
segment_start
: Start time in seconds from call beginning
segment_end
: End time in seconds from call beginning
created_at
: When the transcript was created
Call Status Updates
Sent when the call status changes (start, end, etc.).
{
"type" : "call_status" ,
"call_sid" : "CA123abc..." ,
"timestamp" : "2024-01-01T12:00:00.000Z" ,
"status" : "in-progress" ,
"metadata" : {
"assistant_id" : 123 ,
"started_at" : "2024-01-01T12:00:00.000Z" ,
"to_phone_number" : "+1234567890" ,
"customer_phone_number" : "+0987654321" ,
"call_type" : "inbound"
}
}
Status Values:
in-progress
: Call is active and ongoing
completed
: Call has ended successfully
failed
: Call has failed or was terminated
Error Messages
Sent when an error occurs (e.g., call not found).
{
"type" : "error" ,
"call_sid" : "CA123abc..." ,
"timestamp" : "2024-01-01T12:00:00.000Z" ,
"message" : "Call CA123abc... not found"
}
Client-to-Server Messages
You can send messages to the server for interaction and health checks.
Ping/Pong
Keep the connection alive and test connectivity.
Request Status
Request the current call status on demand.
Send Request
Receive Response
{
"type" : "request_status"
}
Implementation Examples
JavaScript/Browser
Basic Connection
Advanced Handler
const callSid = 'CA123abc...' ;
const apiKey = 'burki_your_api_key_here' ;
const wsUrl = `ws://localhost:8000/live-transcript/ ${ callSid } ?token= ${ apiKey } ` ;
const websocket = new WebSocket ( wsUrl );
websocket . onopen = function ( event ) {
console . log ( 'Connected to live transcript stream' );
};
websocket . onmessage = function ( event ) {
const message = JSON . parse ( event . data );
switch ( message . type ) {
case 'transcript' :
console . log ( ` ${ message . data . speaker } : ${ message . data . content } ` );
displayTranscript ( message . data );
break ;
case 'call_status' :
console . log ( `Call status: ${ message . status } ` );
updateCallStatus ( message );
break ;
case 'error' :
console . error ( `Error: ${ message . message } ` );
break ;
}
};
websocket . onclose = function ( event ) {
console . log ( 'Disconnected from live transcript stream' );
};
Python
Basic Client
Advanced Client
import asyncio
import websockets
import json
async def listen_to_transcript ( call_sid , api_key ):
uri = f "ws://localhost:8000/live-transcript/ { call_sid } ?token= { api_key } "
async with websockets.connect(uri) as websocket:
print ( f "Connected to live transcript for call { call_sid } " )
async for message in websocket:
data = json.loads(message)
if data[ 'type' ] == 'transcript' :
transcript = data[ 'data' ]
print ( f " { transcript[ 'speaker' ] } : { transcript[ 'content' ] } " )
elif data[ 'type' ] == 'call_status' :
print ( f "Call status: { data[ 'status' ] } " )
elif data[ 'type' ] == 'error' :
print ( f "Error: { data[ 'message' ] } " )
# Usage
call_sid = "CA123abc..."
api_key = "burki_your_api_key_here"
asyncio.run(listen_to_transcript(call_sid, api_key))
Node.js
Basic Client
EventEmitter Client
const WebSocket = require ( 'ws' );
const callSid = 'CA123abc...' ;
const apiKey = 'burki_your_api_key_here' ;
const ws = new WebSocket ( `ws://localhost:8000/live-transcript/ ${ callSid } ?token= ${ apiKey } ` );
ws . on ( 'open' , function open () {
console . log ( 'Connected to live transcript stream' );
});
ws . on ( 'message' , function message ( data ) {
const msg = JSON . parse ( data );
switch ( msg . type ) {
case 'transcript' :
console . log ( ` ${ msg . data . speaker } : ${ msg . data . content } ` );
break ;
case 'call_status' :
console . log ( `Call status: ${ msg . status } ` );
break ;
case 'error' :
console . error ( `Error: ${ msg . message } ` );
break ;
}
});
ws . on ( 'close' , function close () {
console . log ( 'Disconnected from live transcript stream' );
});
Key Features
Historical Transcripts
When connecting to an ongoing call, you’ll immediately receive all existing transcripts for that call before new live transcripts start flowing.
Multiple Clients
Multiple clients can connect to the same call’s transcript stream simultaneously without affecting performance.
Automatic Cleanup
Connections are automatically cleaned up when clients disconnect or when calls end, preventing memory leaks.
Error Handling
The server handles various error conditions gracefully:
Call not found
Connection errors
Client disconnections
Invalid message formats
Best Practices
Implement automatic reconnection with exponential backoff
Handle WebSocket close events gracefully
Use ping/pong messages to maintain connection health
Set appropriate timeout values for your use case
Always handle connection errors and implement retry logic
Validate incoming messages before processing
Log errors appropriately for debugging
Implement fallback mechanisms for critical applications
Validate Call SID format before connecting
Implement authentication if required for your use case
Use secure WebSocket connections (WSS) in production
Rate limit connections to prevent abuse
Use Case Examples
Call Monitoring Dashboard
Perfect for building real-time call monitoring interfaces where supervisors can watch multiple calls simultaneously.
class CallMonitoringDashboard {
constructor () {
this . activeConnections = new Map ();
this . callDisplays = new Map ();
}
monitorCall ( callSid ) {
const client = new LiveTranscriptClient (
callSid ,
( transcript ) => this . updateTranscriptDisplay ( callSid , transcript ),
( status ) => this . updateCallStatus ( callSid , status ),
( error ) => this . handleError ( callSid , error )
);
this . activeConnections . set ( callSid , client );
client . connect ();
}
updateTranscriptDisplay ( callSid , transcript ) {
const display = this . getCallDisplay ( callSid );
display . addTranscript ( transcript );
}
stopMonitoring ( callSid ) {
const client = this . activeConnections . get ( callSid );
if ( client ) {
client . disconnect ();
this . activeConnections . delete ( callSid );
}
}
}
Real-time Sentiment Analysis
Analyze call sentiment in real-time using the transcript stream.
import asyncio
from textblob import TextBlob
class SentimentAnalyzer :
def __init__ ( self , call_sid ):
self .call_sid = call_sid
self .sentiment_history = []
async def analyze_transcript ( self , transcript_data ):
text = transcript_data[ 'content' ]
speaker = transcript_data[ 'speaker' ]
# Analyze sentiment
blob = TextBlob(text)
sentiment = blob.sentiment.polarity
self .sentiment_history.append({
'timestamp' : transcript_data[ 'created_at' ],
'speaker' : speaker,
'text' : text,
'sentiment' : sentiment
})
# Alert if sentiment becomes very negative
if sentiment < - 0.5 and speaker == 'user' :
await self .trigger_supervisor_alert()
async def trigger_supervisor_alert ( self ):
print ( f "ALERT: Negative sentiment detected in call { self .call_sid } " )
# Implement your alerting logic here
Live Subtitles/Captions
Provide accessibility features with live captions.
class LiveCaptionService {
constructor ( callSid , captionElement ) {
this . callSid = callSid ;
this . captionElement = captionElement ;
this . transcriptBuffer = [];
this . maxBufferSize = 10 ;
}
connect () {
const client = new LiveTranscriptClient (
this . callSid ,
( transcript ) => this . displayCaption ( transcript )
);
client . connect ();
}
displayCaption ( transcript ) {
// Add to buffer
this . transcriptBuffer . push ( transcript );
// Maintain buffer size
if ( this . transcriptBuffer . length > this . maxBufferSize ) {
this . transcriptBuffer . shift ();
}
// Update display
const captionText = this . transcriptBuffer
. map ( t => ` ${ t . speaker } : ${ t . content } ` )
. join ( ' \n ' );
this . captionElement . textContent = captionText ;
// Auto-scroll to bottom
this . captionElement . scrollTop = this . captionElement . scrollHeight ;
}
}
Production Considerations : In production environments, consider implementing authentication, rate limiting, and monitoring for the WebSocket endpoint to ensure security and reliability.
🔧 Complete Implementation View the complete HTML test client implementation on GitHub