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:
  1. Query Parameter (Recommended): ?token=your_api_key
  2. Authorization Header: Authorization: Bearer your_api_key
  3. 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.
{
  "type": "ping"
}

Request Status

Request the current call status on demand.
{
  "type": "request_status"
}

Implementation Examples

JavaScript/Browser

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

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

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

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