Call Class
The Call object represents a voice call. It’s created by client.newCall() (outbound) or received via telnyx.notification (inbound).
Getting a Call Object
Outbound call
const call = client.newCall({
destinationNumber: '+12345678900',
audio: true,
});
Inbound call
client.on('telnyx.notification', (notification) => {
if (notification.type === 'callUpdate' && notification.call.state === 'ringing') {
const call = notification.call;
call.answer();
}
});
Properties
| Property | Type | Description |
|---|
id | string | Unique call identifier |
state | CallState | Current call state (see Call States) |
direction | 'inbound' | 'outbound' | Call direction |
remotePartyNumber | string | Remote party’s phone number |
remotePartyName | string | Remote party’s display name (if available) |
localPartyNumber | string | Local party’s phone number |
active | boolean | Whether the call is currently active |
recoveredCallId | string | Previous call ID if this call was recovered after reconnection |
peerConnection | RTCPeerConnection | Underlying WebRTC PeerConnection (for advanced use) |
Call States
| State | Description |
|---|
new | Call object created, not yet dialed |
requesting | Outbound INVITE sent to server |
ringing | Inbound: INVITE received. Outbound: remote ringing |
answering | Inbound call being answered (media negotiation) |
active | Call connected — media flowing |
held | Call on hold |
hangup | Call ended (local or remote hangup) |
destroy | Call object cleaned up |
recovering | Call being recovered after reconnection |
Methods
answer()
Answer an incoming call.
client.on('telnyx.notification', (notification) => {
if (notification.type === 'callUpdate' && notification.call.state === 'ringing') {
notification.call.answer();
}
});
Only call answer() when the call state is ringing. Calling answer() on an already-active call creates a duplicate PeerConnection, which causes one-way audio issues.
hangup()
End the call.
// SDK 2.25.x — synchronous
call.hangup();
// SDK 2.26.x — async (returns Promise)
await call.hangup();
See Migration Guide for upgrading from 2.25.x.
muteAudio() / unmuteAudio()
Toggle the microphone.
call.muteAudio(); // Mute
call.unmuteAudio(); // Unmute
hold() / unhold()
Put the call on hold or resume it.
call.hold(); // Put on hold (remote hears hold music)
call.unhold(); // Resume
dtmf(digit)
Send a DTMF tone (0-9, *, #).
call.dtmf('1');
call.dtmf('*');
call.dtmf('#');
sendDigits(digits)
Send a sequence of DTMF digits.
Events
Register event listeners using call.on(eventName, handler):
Call State Events
| Event | Payload | Description |
|---|
telnyx.notification | INotification | Call state updates, media events |
const call = client.newCall({
destinationNumber: '+12345678900',
audio: true,
});
call.on('telnyx.notification', (notification) => {
switch (notification.call.state) {
case 'active':
console.log('Call connected');
break;
case 'hangup':
console.log('Call ended');
break;
}
});
Advanced
Access the PeerConnection
For custom WebRTC monitoring or manipulation:
const pc = call.peerConnection;
// Get current ICE connection state
console.log('ICE state:', pc.iceConnectionState);
// Get current DTLS state
console.log('DTLS state:', pc.connectionState);
// Get stats
const stats = await pc.getStats();
stats.forEach((report) => {
if (report.type === 'candidate-pair' && report.nominated) {
console.log('Nominated pair:', report);
}
});
Direct PeerConnection access is for advanced use cases only. The SDK manages the PeerConnection lifecycle — calling methods like close() or setRemoteDescription() directly may break the call.
Add SIP headers to the INVITE for server-side correlation:
const call = client.newCall({
destinationNumber: '+12345678900',
audio: true,
customHeaders: [
{ name: 'X-Call-Session', value: sessionUuid },
{ name: 'X-Agent-ID', value: agentId },
],
});
Common Patterns
Simple outbound call with state handling
const call = client.newCall({
destinationNumber: '+12345678900',
audio: true,
});
call.on('telnyx.notification', (notification) => {
switch (notification.call.state) {
case 'requesting':
showDialingUI();
break;
case 'ringing':
showRingingUI();
break;
case 'active':
showActiveCallUI();
break;
case 'hangup':
cleanupCallUI();
break;
}
});
// Cancel the call if not yet connected
cancelButton.addEventListener('click', () => {
call.hangup();
});
Inbound call with accept/reject UI
client.on('telnyx.notification', (notification) => {
if (notification.type === 'callUpdate' && notification.call.state === 'ringing') {
const call = notification.call;
showIncomingCallUI({
from: call.remotePartyNumber,
onAccept: () => call.answer(),
onReject: () => call.hangup(),
});
}
});
Hold and resume
// Put call on hold
holdButton.addEventListener('click', () => {
call.hold();
});
// Resume the call
resumeButton.addEventListener('click', () => {
call.unhold();
});
See Also