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.
Call Report Stats
The Telnyx WebRTC JS SDK can automatically collect WebRTC statistics during and after calls. Use call reports to monitor quality, diagnose issues, and build real-time quality indicators.
Enabling Call Reports
const client = new TelnyxRTC({
login_token: jwt,
enableCallReports: true, // Required (default: true)
callReportInterval: 5000, // Stats every 5 seconds (default)
});
| Option | Type | Default | Description |
|---|
enableCallReports | boolean | true | Enable call report collection |
callReportInterval | number | 5000 | Interval in ms between periodic stats |
Real-Time Stats (telnyx.stats.frame)
Fires periodically during an active call (every callReportInterval ms):
client.on('telnyx.stats.frame', (stats) => {
console.log('RTT:', stats.rtt);
console.log('Jitter:', stats.jitter);
console.log('Packet loss:', stats.packetLoss);
});
StatsFrame Properties
| Property | Type | Description |
|---|
rtt | number | Round-trip time in milliseconds |
jitter | number | Jitter in milliseconds |
packetLoss | number | Packet loss percentage |
bytesSent | number | Total bytes sent |
bytesReceived | number | Total bytes received |
packetsSent | number | Total RTP packets sent |
packetsReceived | number | Total RTP packets received |
packetsLost | number | Total RTP packets lost |
audioLevel | number | Current audio level (0.0 - 1.0) |
timestamp | number | Unix timestamp of the measurement |
Quality Thresholds
| Metric | Good | Fair | Poor |
|---|
| RTT | < 150ms | 150-300ms | > 300ms |
| Jitter | < 20ms | 20-50ms | > 50ms |
| Packet Loss | < 1% | 1-3% | > 3% |
Building a quality indicator
client.on('telnyx.stats.frame', (stats) => {
let quality = 'excellent';
if (stats.rtt > 300 || stats.packetLoss > 3) {
quality = 'poor';
} else if (stats.rtt > 150 || stats.packetLoss > 1) {
quality = 'fair';
}
updateQualityIndicator(quality);
});
End-of-Call Report (telnyx.stats.report)
Fires when a call ends with a summary of the entire call:
client.on('telnyx.stats.report', (report) => {
console.log('Call ended:', report.callId);
console.log('Duration:', report.duration, 'seconds');
console.log('Average RTT:', report.avgRtt);
});
Call Report Stats API
For SDK 2.25.20+, call reports are also available via HTTP API:
# Get full call report with ICE data
curl "http://voice-sdk-call-report-stats.query.prod.telnyx.io:4000/api/v1/calls/{user_id}/{call_id}"
# Get ICE candidate data
curl "http://voice-sdk-call-report-stats.query.prod.telnyx.io:4000/api/v1/calls/{user_id}/{call_id}/ice"
API Response Structure
{
"data": {
"call": {
"call_id": "98041520-...",
"duration": 45,
"sdk_version": "2.26.3",
"telnyx_session_id": "...",
"telnyx_leg_id": "..."
},
"segments": [
{
"timestamp": 1712000000,
"bytesSent": 12345,
"bytesReceived": 23456,
"rtt": 45,
"jitter": 3,
"audioLevel": 0.5
}
],
"ice_data": {
"transport": {
"dtls_state": "connected",
"ice_state": "connected",
"srtp_cipher": "AES_CM_128_HMAC_SHA1_80"
},
"selected_pair": {
"local_candidate": { "type": "relay", "ip": "64.16.248.1", "port": 50000 },
"remote_candidate": { "type": "host", "ip": "10.239.207.80", "port": 50001 },
"nominated": true,
"state": "succeeded"
},
"candidates": [
{ "type": "host", "ip": "192.168.1.5", "port": 50000, "timestamp": 1712000000 },
{ "type": "srflx", "ip": "203.0.113.5", "port": 50000, "timestamp": 1712000001 },
{ "type": "relay", "ip": "64.16.248.1", "port": 50000, "timestamp": 1712000002 }
]
},
"logs": ["SDK console log entries if captured"]
}
}
Key Fields for Diagnostics
| Field | Path | What It Tells You |
|---|
| DTLS state | ice_data.transport.dtls_state | "connected" = media encrypted , "connecting" = DTLS stuck |
| ICE state | ice_data.transport.ice_state | "connected" = ICE worked |
| SRTP cipher | ice_data.transport.srtp_cipher | Null = no encryption (DTLS failed) |
| Selected pair | ice_data.selected_pair | Which candidate pair is actually in use |
| Candidate types | ice_data.candidates[].type | host = direct, srflx = STUN, relay = TURN |
Diagnosing Issues from Call Reports
DTLS stuck (“connecting”)
ice_data.transport.dtls_state: "connecting"
ice_data.transport.ice_state: "connected"
ice_data.transport.srtp_cipher: null
Cause: ICE succeeded but DTLS handshake failed. Usually a network issue where DTLS packets from one side aren’t reaching the other (asymmetric routing, multiple NICs, firewall).
Action: Check if client has multiple network interfaces. See Best Practices → Network.
All relay candidates
ice_data.candidates: [
{ type: "relay", ... },
{ type: "relay", ... }
]
Cause: Client can’t generate host or srflx candidates. Likely behind strict NAT or VPN.
Action: Check firewall settings. If expected (e.g., for privacy), set forceRelayCandidate: true.
No candidates at all
Cause: STUN/TURN servers unreachable, or browser denied media permissions before ICE gathering started.
Action: Check network connectivity to stun.telnyx.com and turn.telnyx.com. See Network Requirements.
See Also