Add phone call support to Video SDK apps
Not every participant can join a video call from a browser or app. Sometimes they need to dial in from a phone. Zoom Video SDK's PSTN features let you handle both directions: dialing out to invite someone by phone, or giving participants toll numbers so they can dial in themselves.
This guide covers how to set up dial out, call me, and dial in using Zoom Video SDK. For the complete code you can check out the sample app on GitHub.
Prerequisites
- Node.js & npm LTS
- Zoom Video SDK Account
- Audio Conferencing Plan
- For Video SDK Dial In feature, enable it by contacting support
Installing the Zoom SDK
Install the package via npm:
npm install @zoom/videosdk
Import the SDK in your TypeScript file:
import ZoomVideo, { VideoQuality, DialoutState } from "@zoom/videosdk";
Initialize the client:
const client = ZoomVideo.createClient();
await client.init("en-US", "Global", { patchJsMedia: true });
Implement dial out function to invite
Dial out allows inviting participants by phone. Use the inviteByPhone method on the media stream. Phone number extensions are supported using a hyphen format (e.g., '2025550176-123').
const dialOut = async (
countryCode: string,
displayName: string,
phoneNumber: string,
isCurrentUser: boolean,
) => {
try {
const stream = client.getMediaStream();
stream.inviteByPhone(countryCode, phoneNumber, displayName, {
callMe: isCurrentUser,
});
} catch (error) {
console.error("Dial Out Error:", error);
}
};
Track the call status by listening to the "dialout-state-change" event. The code in the payload maps to the DialoutState enum — use it to react to transitions like ringing, accepted, or success:
client.on("dialout-state-change", (payload) => {
const { code, phoneNumber } = payload;
const stateString = DialoutState[code] || "Unknown";
console.log(`Call to ${phoneNumber}: ${stateString}`);
switch (code) {
case DialoutState.Calling: // 1
case DialoutState.Ringing: // 2
case DialoutState.Accepted: // 3
// Call is in progress
break;
case DialoutState.Success: // 8
// Phone audio is now connected to the session
break;
case DialoutState.Fail: // 4
case DialoutState.Busy: // 5
case DialoutState.NotAvailable: // 6
// Call did not connect — update your UI accordingly
break;
}
});
You can hang up an active phone call using hangup or cancel an in-progress dial-out call with cancelInviteByPhone:
const hangUp = async () => {
const stream = client.getMediaStream();
await stream.hangup();
};
const cancelDialOut = async (countryCode: string, phoneNumber: string) => {
const stream = client.getMediaStream();
stream.cancelInviteByPhone(countryCode, phoneNumber);
};
When you're done, clean up the event listener to avoid memory leaks:
client.off("dialout-state-change", dialOutStateChange);
Implement call me option
If a participant wants to use their phone for audio instead of their computer, they can have the system call them. Set the callMe option to true in the inviteByPhone method:
stream.inviteByPhone(countryCode, myPhoneNumber, displayName, { callMe: true });
The dial-out state change handler from the previous section will track the call status for this flow as well. When code equals DialoutState.Success, the participant's phone audio is connected to the session.
Retrieve dial in information
Dial in enables participants to join via phone using toll numbers. After joining a session, retrieve and display the dial-in information for participants to call in manually.
To enable this PSTN feature, you must create a Video SDK session using the Zoom API before joining. This is crucial because getCurrentSessionCallinInfo() only works with sessions created via the API.
First, create a session using the endpoint https://api.zoom.us/v2/videosdk/sessions:
// Important: Generate the JWT server-side to avoid exposing credentials in client code
async function createVideoSession(jwt: string): Promise<void> {
const url = "https://api.zoom.us/v2/videosdk/sessions";
const body = {
session_name: "TestSession",
session_password: "",
settings: {
auto_recording: "none",
},
};
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwt}`,
},
body: JSON.stringify(body),
});
if (!response.ok) {
throw new Error(
`Failed to create session: ${response.status} ${response.statusText}`,
);
}
const data = await response.json();
// Session created successfully
}
After joining the session with the client, retrieve and display the call-in info:
const showMeetingInfo = async () => {
const { meetingId, password, tollNumbers } = client
.getMediaStream()
.getCurrentSessionCallinInfo();
const tollList = tollNumbers
.map((toll: any) => `${toll.countryName}: ${toll.number}`)
.join("\n");
// Display the meeting ID, password, and toll numbers in your UI
console.log(`Meeting ID: ${meetingId}`);
console.log(`Password: ${password ?? "No password set"}`);
console.log(`Toll Numbers:\n${tollList}`);
};
Display the meeting ID, password, and toll numbers in your UI so participants know what to dial.
Next steps
For a working implementation with UI components (phone number validation, popup modals, video tiles), check out the GitHub repository. The repo includes a complete app with dial out, call me, dial in, and hangup flows wired together.