How to Build a Voice Proxy in Node.js Using Voice API
最終更新日 November 28, 2024

Have you ever ordered food from a delivery service and needed to give the driver access to your building? You might hesitate to share your personal number, but you still want to ensure your food arrives. With masked calling, both you and the driver can communicate without revealing your actual phone numbers. The driver sees a temporary number, and so do you, protecting both parties' privacy.

Classified Ads App PreviewClassified Ads App Preview

In this tutorial, we’ll do just that and use Vonage’s Masked Calling feature. We will deploy this application on Vonage Cloud Runtime, eliminating the need to manage servers and host on your own.

Prerequisites

  • A Vonage developer account

  • A Vonage phone number. This can be purchased using the free credit you received when creating an account. Go to your API Dashboard. On the left panel, under ‘Build & Manage’, go to Numbers > Buy Numbers. Search for a phone number that meets your requirements (make sure ‘Voice’ is one of the Features) and click Buy.

Luckily, this is really easy to do using Vonage Voice API because we have a prebuilt Masked Calling API on Code Hub that you can instantly deploy! We’ll be going through how to take this route.

How to Create and Deploy Your Application

After signing into your Vonage developer account, go to the Advanced Masked Calling page. Under the Deploy Code tab, click Deploy new instance.

Select the Region and give your Instance a name. Click Assign a number and choose the Vonage phone number that you would like to link to your application. Click Continue. Your screen should now look like this:

Screenshot of Advanced Masked Calling application page with a new instance running with the buttons ‘Launch’, ‘View logs’ and ‘Delete’ next to them.Code Hub DeployedClick Launch to run the application. You will be prompted to enter your User credentials. Both your API Key and Password (your API secret) can be found on your API Dashboard.

You will then see your new application under ‘Deployed Instances’. Click ‘Launch’ to run it, and input your Vonage API Key and Secret to view the application. You can view the API specification in the "API" tab.

Once you’re logged in, you can see the application.

Screenshot of Masked Calling API browser applicationPhone Number Pairing

Here, you can add two phone numbers and set an expiration date for how long you'd like them to use the call masking feature. Click Add, and they should appear in your paired phone number list. If you end up adding more phone number pairings and want to find a specific pair, type the phone number you’re looking for and click Search to find its match.

When either of these numbers calls the Vonage number linked to this application (you can confirm the linked number under the Settings tab of the browser application), the recipient will see the call as coming from the Vonage number, not the caller’s actual number. Similarly, the caller dials the Vonage number and won’t know the recipient’s actual number either.

Try It Out

Assuming you listed your own phone number as Phone number 1, use your personal phone to call the Vonage phone number linked to this application. The person who owns Phone Number 2 should receive a call showing the Vonage phone number instead of your actual number. Go ahead and give it a shot now!

How to Customize the Code

If you decide you want to customize the prebuilt application directly on Vonage Cloud Runtime, go to the Get Code tab on the Advanced Masked Calling page and click Create a new development environment. Select your region, give your workspace a name, and choose the Vonage phone number you want to link to this application.

Screenshot of the Masked Calling API project on Vonage Cloud RuntimeMasked Calling READMEOnce the workspace is open, you'll see that the sample app is ready to go! However, you can make your own changes in the code directly from this IDE.

To run your updated project, open the Terminal tab and run this command:

npm run

Once you’re ready to deploy it, run this command:

vcr deploy
Here's how the call flow works:
  • The user adds phone numbers (numberOne, numberTwo) and sets the expiration (expiryTime) of the pairing on the website (front-end).

  • pairNumbers creates the pair and sets expiry.

async function pairNumbers(numberOne, numberTwo, expiryTime, state) {
   try {
       await state.set(numberOne, numberTwo);
       await state.set(numberTwo, numberOne);

       const expirySeconds = getTimeInSeconds(expiryTime);
       await state.expire(numberOne, expirySeconds);
       await state.expire(numberTwo, expirySeconds);


       await state.mapSet('pairs', { [numberOne]: JSON.stringify({ numberTwo, expiryTime }) });
   } catch (e) {
       return false
   }
}
  • The onCall endpoint is triggered after waiting for an incoming call to the Vonage number.

await voice.onCall('/onCall');
await voice.onCallEvent('/onCallEvent');
  • findPair retrieves paired numbers.

async function findPair(number, state) {
   const numbersSet = new Set();
   let pairObj = await state.mapGetValue('pairs', number);


   if (!pairObj) {
       // The pair is stored using the other number
       let pairedNumber = await state.get(number);
       numbersSet.add(pairedNumber);
       pairObj = await state.mapGetValue('pairs', pairedNumber);
   }
  • If the pair exists and is not expired, connect the call.

app.post('/onCall', async (req, res, next) => {
   try {
       const pairedNumber = await state.get(req.body.from);


       if (!pairedNumber) {
           res.json([{
               action: 'talk',
               text: 'This number has not been configured yet.'
           }]);
           return;
       }
  • If there is no pair or an expired pair, handle it accordingly (e.g., the caller will hear the error message).

app.post('/onCall', async (req, res, next) => {
   try {
       const pairedNumber = await state.get(req.body.from);


       if (!pairedNumber) {
           res.json([{
               action: 'talk',
               text: 'This number has not been configured yet.'
           }]);
           return;
       }
  • After expiration, the pair is automatically deleted.

app.delete('/pairs', validateRequestMiddleware, async (req, res, next) => {
   try {
       const numberOne = req.body.number_one;
       const numberTwo = req.body.number_two;


       if (!numberOne || !numberTwo) {
           res.sendStatus(400);
           return;
       }


       await state.delete(numberOne);
       await state.delete(numberTwo);
       await state.mapDelete('pairs', [numberOne, numberTwo]);
       res.sendStatus(200);
   } catch (e) {
       next(e);
   }
});

You can include this working code in your own application. However, if you decide not to deploy your project to Vonage Cloud Runtime, there are some areas to consider. Refer to the documentation to implement an approach that does not use Vonage Cloud Runtime.

Join the Party

And just like that, you’ve deployed a Voice Proxy on Vonage Cloud Runtime! You can level up the application even further by adding your own features. Our developer community is growing on Slack, and we’d love for you to be a part of it. If you end up trying this tutorial out, I’d love to hear your thoughts. Feel free to tag me on X, formerly known as Twitter, and follow our team on there, too. Happy coding!

Diana PhamDeveloper Advocate

Diana is a developer advocate at Vonage. She likes eating fresh oysters.

Ready to start building?

Experience seamless connectivity, real-time messaging, and crystal-clear voice and video calls-all at your fingertips.

Subscribe to Our Developer Newsletter

Subscribe to our monthly newsletter to receive our latest updates on tutorials, releases, and events. No spam.