# 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](https://github.com/zoom/videosdk-web-pstn). ## Prerequisites - Node.js & npm LTS - Zoom Video SDK Account - [Audio Conferencing Plan](https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0064472) - For Video SDK Dial In feature, enable it by [contacting support](https://developers.zoom.us/support/) ## Installing the Zoom SDK Install the package via npm: ```bash npm install @zoom/videosdk ``` Import the SDK in your TypeScript file: ```typescript import ZoomVideo, { VideoQuality, DialoutState } from "@zoom/videosdk"; ``` Initialize the client: ```typescript 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'`). ```typescript 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: ```typescript 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`: ```typescript 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: ```typescript 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: ```typescript 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`: ```typescript // Important: Generate the JWT server-side to avoid exposing credentials in client code async function createVideoSession(jwt: string): Promise { 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: ```typescript 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](https://github.com/zoom/videosdk-web-pstn). The repo includes a complete app with dial out, call me, dial in, and hangup flows wired together.