In this blog post, we highlight how phone number masking helps build consumer trust in the services marketplace at Yelp, decreases the friction in communication with service professionals, and allows for seamless switching between the Yelp app and a user’s phone. We present a high level overview of our in-house phone masking system and dive into the details of the engineering challenge of optimizing the usage of proxy phone number resources at Yelp’s scale.

Background

Every year, millions of requests for quotes, consultations or other messages are sent to businesses on Yelp. Those users choose to use Yelp to connect with local services professionals for their projects because they value our trustworthy reviews and seamless search experience. Yelp also provides users with a dedicated project workspace where they can outline their request, use our Request a Quote product to get matched with relevant businesses, and use our in-app messaging platform to easily compare quotes and communicate with pros.

While the messaging platform is a convenient tool for communication, we’ve observed the following pain points:

  • Some customers, especially new users, may not be in the habit of checking the Yelp app for new business replies.
  • Businesses may sometimes feel that communicating via a phone call is more engaging and personal.
  • When it comes to more urgent or complex projects, it can be inefficient to describe the issue in a message.

To remedy these pain points, we’ve seen a lot of businesses simply ask for the consumer’s phone number. While this solves the problems above, many customers may feel reluctant to share their contact information out of concern for receiving spam calls and unsolicited promotional messages. They want to feel confident that they can trust the business before providing a phone number.

Masked Phone Communication for Services Projects

To facilitate communication between customers and businesses via phone calls, while providing peace of mind that the user’s number is protected, Yelp recently introduced an evolution of the Request a Call feature where customers can communicate with pros via masked phone numbers through both calls and SMS. Upon submitting a Request a Quote, the user can opt-in to receiving calls and texts about their project. If they opt-into sharing their number, Yelp assigns a temporary masked number to the customer and the business, which allows both parties to communicate seamlessly through calls, SMS, and the Yelp app.

After the customer shares and enters their phone number (left), the business can call and text the customer’s masked phone number (right).

Masked phone numbers provide the following benefits over calling or texting directly:

  • Privacy: Neither party’s real phone number is shared with the other, and both can opt-out of communicating via phone calls at any time.
  • Protection: Masked numbers cannot be shared with third parties—only the business can reach the customer through the masked number, and vice versa.
  • Continuity: The full history of texts and calls is mirrored on both the app and the user’s phone, which allows for easy switching between the communication channels.

The conversation history between the customer and the business is synced between the SMS messages and the Yelp messaging platform.

In the next sections, we’ll take you through a high level overview of Yelp’s phone masking process, and highlight the key technical design decisions that we made in order to build consumer trust and provide the convenient benefits outlined above, while minimizing system costs and enabling the system to be scaled to Yelp’s large user base.

Integrating with a Telephony API

Fortunately for us, when it comes to working with phone numbers there is no need to start from scratch. Telephony API providers make it easy to purchase phone numbers, send or receive SMS messages, and initiate or receive phone calls. Additionally, they allow a phone number’s owner to react immediately to any event that occurs on the number, like an incoming call, through sending webhooks to a custom URL and accepting a response with custom instructions on how to handle the event. For example, the incoming call can get an automatic response or could be redirected to another number.

Using these building blocks, setting up a phone number masking application is straightforward. Two parties can have a phone call or engage in an SMS conversation through a proxy number without revealing their real numbers. We can simply forward the messages from one number to the other when receiving a webhook. And it all works seamlessly as if you’re communicating directly with the other person.

Below is a high-level architecture of how Yelp integrated with a telephony API provider to offer the utility of phone masking, while keeping all phone events in sync with the Yelp conversation.

For calls, we proxy the call immediately to the second number and reflect the call on the user’s inbox. For SMS, the flow looks very similar, except instead of a call we persist and forward a text message.

For calls, we proxy the call immediately to the second number and reflect the call on the user’s inbox. For SMS, the flow looks very similar, except instead of a call we persist and forward a text message.

Masking Session Data Model

The only thing left is an appropriate data model to integrate phone masking with the Yelp conversation. Our key requirement is that only the Yelp business can call the proxy number to reach the customer, and vice versa. Therefore, we need a data model which encapsulates the customer and business numbers and links them via a proxy number, so that we can route messages and calls to the proxy number to the intended recipient. We call this model a “masking session” because it provides a temporary connection between the two real numbers while not exposing them directly to each other. It looks like this at a high level:

Minimal masking session data model. Each of Yelp’s Services conversations has an associated session which allows us to route messages and calls between the numbers seamlessly, while reflecting the message and call events on the conversation feed.

Minimal masking session data model. Each of Yelp’s Services conversations has an associated session which allows us to route messages and calls between the numbers seamlessly, while reflecting the message and call events on the conversation feed.

How to Scale Phone Masking without Breaking the Bank?

The diagram above is a good high-level outline for how phone masking works. But in reality Yelp connects hundreds of thousands of businesses and customers every month, and this model requires that we allocate one number for every connection that we facilitate.

The most basic proxy number allocation strategy is to assign a unique number for every masking session.

The most basic proxy number allocation strategy is to assign a unique number for every masking session.

This can quickly get prohibitively expensive, not to mention that phone numbers are a finite resource and even the telephony API provider couldn’t provide us with that many numbers.

There is a solution though, and it is 2-fold: recycle and reuse.

In the following few sections, we walk through several possible phone number allocation strategies that recycle and reuse proxy numbers between sessions in various ways. Ultimately, we arrive at the one that minimizes the size of the proxy number pool that we need to maintain in order to support our system.

Phone number recycling

For certain use cases such as delivery apps or connecting with your rideshare driver, the masking session needs to be relatively short lived (typically several minutes). In those situations an application can get away with having a relatively small pool of proxy numbers by using a very aggressive recycling policy. The two determining factors for the size of the proxy number pool are the number of sessions needed per unit time and the average lifespan of a session.

For example, if a delivery app has on average 1000 deliveries per hour, typically lasting under 30 minutes, then it would need 500 proxy numbers on average at a given time.

Yelp’s phone masking system implements recycling, but we need to keep sessions active for a longer period of time given conversations between customers and businesses often last for weeks. There is a potential workaround where we recycle a number after N hours of inactivity and then allocate a new number if the conversation resumes. However, then we could risk breaking the continuity of the SMS conversation if the later messages start coming from a new number, and we may cause confusion when a conversation with a new business starts abruptly from the same number. Because of these considerations, we typically mark a proxy number as recyclable only after 30 days.

Therefore, we only ever need to maintain as many phone numbers as the number of connections per month, i.e., our costs scale as O(conversations per month). This is definitely an improvement, but it still requires purchasing millions of phone numbers, which means that we need further optimizations to our phone number use.

Phone number reuse

The idea behind proxy phone number reuse is to use the same number in multiple masking sessions simultaneously instead of it only taking part in one session at a time. The tricky part is to assign the numbers in such a way that all phone calls and texts are routed unambiguously to the intended recipients. Below we describe some options we evaluated.

Unique number for every business

One approach would be to not actually use a unique number for every conversation, but instead have a constant proxy number for each business on Yelp, such that two different customers see the same number for a given business, and we can disambiguate the conversation based on the sender/caller number. It is also quite natural that the business number doesn’t change between different users.

With this proxy number allocation strategy we assign a unique proxy number to each business. Then all customers that have conversations (masking sessions) with the same business always interact with the same proxy number.

With this proxy number allocation strategy we assign a unique proxy number to each business. Then all customers that have conversations (masking sessions) with the same business always interact with the same proxy number.

Unfortunately, this approach still has a couple of problems. First, it doesn’t allow the business to call or text the proxy number because we wouldn’t know which customer we should forward the call to. (This problem is actually solvable with the two-number-pools approach we describe later). And second, it scales as O(businesses using Request a Quote) which still doesn’t reduce costs sufficiently, but it’s closer to the optimal solution.

Each party sees unique numbers (single proxy pool)

What’s interesting about the unique number per business approach is that it touches on the actual constraints at hand. Namely they are:

  • Constraint 1: Each customer should be interacting with a different number for each different business they are contacting.
  • Constraint 2: Each business owner should be interacting with a different number for each different customer they are working with.

However, there is no problem if two different customers see the same number for two different businesses because we can disambiguate who they are calling/texting based on the caller phone number. The same holds true for the business side. Therefore, we only need enough numbers to satisfy the above constraints for all of the customer-business connections every month at Yelp (with recycling).

We can demonstrate how this works out to be a small number with a hypothetical example. If most customers contact less than 10 businesses per month, and most businesses receive less than 100 requests per month1, we only need 100 numbers (max of the two) to satisfy both constraints. We can also add a safety factor to account for outliers, but the number pool size still ends up being a small constant size.

More importantly, this allocation strategy minimizes our proxy number costs because the number pool size does not need to increase (it is O(1)) with the volume of customer quote requests sent on Yelp or the number of businesses we onboard on the platform.

In this example, there are 2 customers and 2 businesses having a total of 4 unique conversations (masking sessions), facilitated by a pool of 2 proxy numbers. Notice how the proxy numbers are mapped to participants such that each party sees unique numbers for each of their conversations (i.e. both constraints are satisfied).

In this example, there are 2 customers and 2 businesses having a total of 4 unique conversations (masking sessions), facilitated by a pool of 2 proxy numbers. Notice how the proxy numbers are mapped to participants such that each party sees unique numbers for each of their conversations (i.e. both constraints are satisfied).

Each party sees unique numbers (multiple proxy pools)

As a final improvement, we actually use two pools of proxy numbers, one for the customer side and another for the business side. This way the masking is still seamlessly maintained because the customer always communicates with the same number and so does the business owner. They just happen to be different numbers. The final masking session model looks like this:

Like before, there are 2 customers and 2 businesses having a total of 4 unique conversations (masking sessions), but now they are facilitated by 2 pools of 2 proxy numbers each. Each party still sees unique numbers for each of their conversations, but the customer and business in a particular session see distinct proxy numbers (each from their respective pool).

Like before, there are 2 customers and 2 businesses having a total of 4 unique conversations (masking sessions), but now they are facilitated by 2 pools of 2 proxy numbers each. Each party still sees unique numbers for each of their conversations, but the customer and business in a particular session see distinct proxy numbers (each from their respective pool).

This strategy still satisfies both constraints and keeps costs constant, but it has the following benefits:

  • Less risk of exhausting numbers: Customer proxy numbers only need to satisfy constraint 1 from the previous section while business numbers only need to satisfy constraint 2. This makes it less likely to run out of proxy numbers to assign to a session. The two constraints become increasingly harder to be satisfied together by a single number the more sessions we create.
  • Simpler allocation and routing logic: The code is easier to maintain and understand.
  • Greater flexibility: We can configure each number pool independently. For example, each pool can have a different size, a distinct path for webhooks, specific alerting, etc. We could even change the assignment strategy of each pool if necessary, or we can have additional pools if we needed a different assignment strategy for a new participant type. (E.g. having a constant number per business like mentioned above for the customer side for specific subsets of businesses).

The only downside of this final strategy is that we need to purchase slightly more proxy numbers overall. However, this tradeoff is worth it given the added flexibility and ease of maintenance.

Conclusion

In this blog post we learned how Yelp’s engineering team developed an in-house phone masking system for the Services Marketplace. The feature helps us uphold our core value of “Protecting the Source” by prioritizing the privacy of consumers when connecting them with professionals over the phone, and maintains professionals’ trust that Yelp connects them with high-intent customers who are more eager to get their projects done.

At the same time, it poses an interesting technical challenge to prevent costs from increasing linearly with the volume of traffic. We managed to overcome this problem through good data modeling and intelligent allocation of resources, which allows us to offer the convenience and flexibility of masked phone communication for all Request a Quote projects.

Acknowledgements

This project required significant cross-team collaboration, and I would like to thank everyone in the Services group and other Yelp teams who contributed to the development and made it possible. Special thanks goes to Yi Qi, Billy Barbaro, James Coles-Nash, Michelle Tan, and Rich Schreiber for your technical and editorial reviews of this article.

Footnotes

1: These numbers are for illustrative purposes only.

Become an Engineer at Yelp

We work on a lot of cool projects at Yelp. If you're interested, apply!

View Job

Back to blog