Documentation Index
Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
Use this file to discover all available pages before exploring further.
Production Best Practices
Going from “it works on my machine” to “it works for all users, reliably” requires addressing security, reliability, performance, and monitoring. This guide covers the key areas.
Authentication
Use JWT in production
// Production
const client = new TelnyxRTC({ login_token: jwt });
// Development only
const client = new TelnyxRTC({ login: 'user', password: 'pass' });
JWTs are time-limited, revocable, and don’t expose passwords. See Authenticating Your App.
Generate JWTs on your backend
// Backend generates token — API key never reaches the browser
app.post('/api/telnyx-token', async (req, res) => {
const token = await telnyx.telephonyCredentials.createToken(credentialId);
res.json({ token });
});
// Never do this — API key in browser source
const response = await fetch('https://api.telnyx.com/v2/telnyx_rtc/access_tokens', {
headers: { Authorization: `Bearer ${API_KEY}` }, // API_KEY exposed!
});
Handle token refresh
client.on('telnyx.notification', (notification) => {
if (notification.type === 'userMediaError') return;
// Check for token expiring soon
if (notification.type === 'callUpdate' && notification.call?.state === 'destroyed') {
// If disconnected due to auth, try refresh
}
});
// Or use the session event
client.on('telnyx.ready', () => {
console.log('Connected and authenticated');
});
One credential per user
Never share a Telephony Credential across multiple users. Each user must have their own JWT to ensure they receive their own incoming calls.
Connection Management
One client instance per tab
// Create once
let client = null;
function getClient() {
if (!client) {
client = new TelnyxRTC({ login_token: getJwt() });
client.connect();
}
return client;
}
// Creating multiple instances
const client1 = new TelnyxRTC({ login_token: jwt }); // WebSocket 1
const client2 = new TelnyxRTC({ login_token: jwt }); // WebSocket 2 — wasteful
Clean up on page unload
window.addEventListener('beforeunload', () => {
if (client) {
client.calls.forEach(call => call.hangup());
client.disconnect();
}
});
Handle reconnection gracefully
client.on('telnyx.notification', (notification) => {
if (notification.type === 'callUpdate') {
const call = notification.call;
if (call.state === 'reconnecting') {
showBanner('Connection lost. Reconnecting...');
} else if (call.state === 'active' && wasReconnecting) {
hideBanner();
}
}
});
See Handle Reconnection for the full guide.
Audio Quality
Request microphone with constraints
const call = client.newCall({
destinationNumber: '+12345678900',
audio: true,
// SDK handles getUserMedia internally
});
If you need to control the microphone before making a call:
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
},
});
Monitor call quality
Enable call reports in production:
const client = new TelnyxRTC({
login_token: jwt,
enableCallReports: true,
callReportInterval: 5000,
});
Set up quality alerts:
client.on('telnyx.notification', (notification) => {
if (notification.type === 'callQuality') {
const { mos, rtt, jitter, packetLoss } = notification.callQuality;
if (mos < 3.0 || rtt > 500 || jitter > 100 || packetLoss > 5) {
logQualityIssue(notification);
}
}
});
Recommend headphones for agents
Built-in speakers + microphone create echo. For call center agents, recommend USB headsets or enforce echo cancellation.
Network Configuration
Allowlist Telnyx domains
Ensure your firewall allows:
| Domain | Port | Protocol | Purpose |
|---|
rtc.telnyx.com | 443 | WebSocket (TLS) | Signaling |
stun.telnyx.com | 3478 | UDP | STUN (ICE) |
turn.telnyx.com | 3478 | UDP | TURN relay |
turn.telnyx.com | 443 | TCP | TURN fallback |
api.telnyx.com | 443 | HTTPS | REST API |
Don’t force relay unless necessary
// Only if you have a specific security requirement
const client = new TelnyxRTC({
login_token: jwt,
forceRelayCandidate: true, // Forces all media through TURN
});
// Default — lets ICE find the best path
const client = new TelnyxRTC({
login_token: jwt,
});
Forcing relay adds 20-80ms latency per direction. Use it only when corporate policy requires all media to go through a relay.
See Configure Network & Firewall for the full guide.
Error Handling
Always handle errors
client.on('telnyx.notification', (notification) => {
if (notification.type === 'userMediaError') {
const { code, message } = notification.error;
switch (code) {
case 1: // Not allowed
showError('Microphone access denied. Please allow access in browser settings.');
break;
case 2: // Not found
showError('No microphone detected. Please connect a microphone.');
break;
case 3: // Not readable
showError('Microphone in use by another application.');
break;
}
}
});
Handle connection failures
client.on('telnyx.socket.close', () => {
showError('Connection to Telnyx lost. Attempting to reconnect...');
});
client.on('telnyx.socket.error', (error) => {
logError('WebSocket error', error);
});
Don’t show raw errors to users
// Technical error exposed to user
showError(`Call failed: ${error.message}`);
// User-friendly message
showError('Unable to connect the call. Please try again.');
Memory Management
Clean up call references
client.on('telnyx.notification', (notification) => {
if (notification.call?.state === 'destroyed') {
// Remove call from your state
removeCallFromState(notification.call.id);
}
});
Remove event listeners
// When component unmounts (React example)
useEffect(() => {
const handler = (notification) => { /* ... */ };
client.on('telnyx.notification', handler);
return () => {
client.off('telnyx.notification', handler);
};
}, []);
Monitoring & Observability
Enable call reports
const client = new TelnyxRTC({
login_token: jwt,
enableCallReports: true, // Auto-upload reports after each call
callReportInterval: 5000, // Stats every 5 seconds
});
Track key metrics
| Metric | Good | Warning | Critical |
|---|
| MOS | > 4.0 | 3.0–4.0 | < 3.0 |
| RTT | < 150ms | 150–300ms | > 300ms |
| Jitter | < 20ms | 20–50ms | > 50ms |
| Packet Loss | < 1% | 1–3% | > 3% |
Log quality issues server-side
client.on('telnyx.notification', (notification) => {
if (notification.type === 'callQuality') {
sendToMonitoring({
callId: notification.call.id,
mos: notification.callQuality.mos,
timestamp: Date.now(),
});
}
});
Deployment Checklist
| Requirement | Details |
|---|
| Authentication uses JWT | login_token in production, not login+password |
| JWT generated on backend | API key never in browser |
Token refresh handles TOKEN_EXPIRING_SOON | Call client.updateToken() on warning code 34001 |
beforeunload disconnects client | Hang up calls and call client.disconnect() |
enableCallReports: true | Automatic call reports for production monitoring |
| Error handling covers key events | userMediaError, socket.close, socket.error |
| Firewall allows Telnyx domains | rtc.telnyx.com:443, stun.telnyx.com:3478, turn.telnyx.com:443 |
Reconnection UI shown during reconnecting state | Users should see connection status |
Call references cleaned up on destroyed state | Prevent memory leaks |
No forceRelayCandidate: true unless required | Forced relay adds 20-80ms latency |
| Quality metrics logged to monitoring | Track MOS, RTT, jitter, packet loss |
| User-friendly error messages | No raw errors shown to users |
See Also