Build Number Masking into your application with MessageBird

marcel corso
MessageBird
Published in
6 min readJul 30, 2019

⏱ 30 min build time | View the in-depth tutorial | Download the code

In an age where everything from ride-sharing to food delivery is available on-demand, we frequently find ourselves interacting with strangers through our phones to complete a number of transactions. In these interactions, maintaining a level of privacy for both customers and employees is crucial, not only to protect users’ identities but also to ensure that transactions continue to happen in-app and not elsewhere.

Fortunately, MessageBird offers a solution that allows employees and customers to reach each other without exposing any of their personal contact details. By building Number Masking into your application, you protect the privacy of all your users by anonymizing all customer-employee communications enabled through your app.

To show you how to build Number Masking into your application, we’ll take you through the steps of building a proxy system to mask phone numbers for a fictitious ride-sharing platform we’ve named BirdCar.

This tutorial is in PHP, but you can find a more detailed version of this tutorial with more languages available here.

Getting Started

So, you want to enable your employees and customers to reach each other without accessing each other’s contact details. How do you do it? By putting a third phone number or a pool of numbers in between the two endpoints of a given call.

Prerequisites for receiving calls

The BirdCar system receives incoming calls and forwards them. To do this, our application defines a webhook URL, which you’ll then assign to a number purchased in the MessageBird Dashboard using Flow Builder. Every time someone calls that number, MessageBird collects the call and forwards its information to the webhook URL, where our app can process it.

In this example, our application would be exposed on the internet at Birdcar.com, so we’d create a webhook there, where MessageBird will then notify our app that a call has been made.

Getting an inbound number

Head over to the Numbers section on the left-hand side of your MessageBird Dashboard and select Buy a number. Pick the country in which you and your customers are located, and make sure ‘Voice capabilities’ is selected. Nice! You’ve just purchased your own MessageBird number.

Connecting the number to a webhook for voice

Let’s head over to the Flow Builder.

STEP 1

Click on ‘Create new flow’ and then ‘Create Custom Flow’.

STEP 2

Give your flow a name, choose ‘Phone Call’ as the trigger and hit ‘Next’.

STEP 3

Click on the first step ‘Phone Call’ and select the number or numbers you’d like to which you’d like to attach this flow (probably the number you just bought).

STEP 4

Add a new step by pressing the small ‘+’, choose ‘Fetch call flow from URL’ and paste the full webhook URL. In our example, that’s https://birdcar.com/webhook-voice. We’ll use the path /webhook-voice to handle incoming calls in our sample application. Hit ‘Save’ when you’re ready.

STEP 5

Presto! Hit ‘Publish’ at the top right of the screen to activate your flow. Your flow should look something like this:

You’re done setting up flows for your application! Now, we can begin writing code in your application to handle the requests going to /webhook-voice.

Configuring the MessageBird SDK

Grab the MessageBird PHP SDK by using composer like this

composer require messagebird/php-rest-api

Then, initialize the client with

new MessageBirdClient(getenv(‘MESSAGEBIRD_API_KEY);

where MESSAGEBIRD_API_KEY is an environment variable that has a RESTful API key. You can retrieve your API key from the MessageBird Dashboard.

Let’s assume there’s a table with all our available numbers, and that each incoming call will find a number from this table. To learn how to set this table up, check out the full-length tutorial.

Finding a number

We need to access a number that hasn’t been assigned to a ride for the customer or the driver. To check this, let’s write a SQL query with two subqueries:

  • Find all numbers for rides from the selected customer (subquery 1)
  • Find all numbers for rides from the selected driver (subquery 2)
  • Find all numbers that are in neither of those lists and return one of them (main query)

The SQL query looks like this in our PHP code:

// Find a number that has not been used by the driver or the customer
$stmt = $this->db->prepare(‘SELECT * FROM proxy_numbers ‘
. ‘WHERE id NOT IN (SELECT number_id FROM rides WHERE customer_id = :customer) ‘
. ‘AND id NOT IN (SELECT number_id FROM rides WHERE driver_id = :driver)’);
$stmt->execute([
‘customer’ => $customer[‘id’],
‘driver’ => $driver[‘id’]
]);
$proxyNumber = $stmt->fetch();

It’s possible that no proxy number was found. In that case, we alert the admin that the number pool is depleted and they should buy more numbers:

if ($proxyNumber === false) {
// No number found!
return “No number available! Please extend your pool.”;
}

Receiving and forwarding voice calls

When a customer or driver calls the proxy number from which they received the confirmation, the system should transfer the call to the other party. As we have instructed MessageBird to fetch instructions from /webhook-voice we need to implement the $app->get(‘/webhook-voice’) route. Keep in mind that custom call flows are always retrieved with GET.

First, the input sent from MessageBird should be read because we’re interested in the source and destination of the call so that we can find the ride based on this information:

// Handle incoming calls
$app->get(‘/webhook-voice’, function($request, $response) {
// Read input sent from MessageBird
$number = $request->getQueryParam(‘source’);
$proxy = $request->getQueryParam(‘destination’);

As we’ll return a new call flow encoded in XML format, we set the response header accordingly and start writing the response:

// Answer will always be XML
$response = $response->withHeader(‘Content-Type’, ‘application/xml’)
->write(‘<?xml version=”1.0" encoding=”UTF-8"?>’);

Looking up receiver

To find the ride, we use an SQL query which joins all four tables. We’re interested in all entries in which the proxy number matches the recipient field from the webhook, and the originator matches either the driver’s number or the customer’s number:

// Find potential rides that fit the numbers
$stmt = $this->db->prepare(‘SELECT c.number AS customer_number, d.number AS driver_number, p.number AS proxy_number ‘
. ‘FROM rides r JOIN customers c ON r.customer_id = c.id JOIN drivers d ON r.driver_id = d.id JOIN proxy_numbers p ON p.id = r.number_id ‘
. ‘WHERE proxy_number = :proxy AND (driver_number = :number OR customer_number = :number)’);
$stmt->execute([
‘number’ => $number,
‘proxy’ => $proxy
]);
$row = $stmt->fetch();

Transferring call

To transfer the call, we return a short snippet of XML to MessageBird, and also log the action to the console:

// Create call flow to instruct transfer
error_log(“Transferring call to “ . $destination);
$response->write(‘<Transfer destination=”’ . $destination . ‘“ mask=”true” />’);

The <Transfer /> element takes two attributes: destination indicates the number to transfer the call to — which we’ve determined as described above — and mask instructs MessageBird to use the proxy number instead of the original caller ID.

If we don’t find a ride, we return a different XML snippet with a <Say /> element, which is used to read some instructions to the caller:

} else {
// Cannot match numbers
$response->write(‘<Say language=”en-GB” voice=”female”>Sorry, we cannot identify your transaction. Make sure you call in from the number you registered.</Say>’);
}

This element takes two attributes, language and voice, that define the configuration for speech synthesis. The text itself goes between the opening and closing XML element.

And that’s it! You’ve just built your own number masking system with MessageBird using PHP.

Time to start building 🚀

Want to build something similar but not quite sure how to get started? Please feel free to let us know at support@messagebird.com; we’re here to help!

--

--