Mask an incoming call phone number with ASP.NET Core 6.0

If you're creating an app that connects strangers to each other, it's very likely you'll want to hide the phone numbers of the users of the app from each other, to keep information private and everyone involved safe and secure. Using the Voice API and the Sinch SDK for C#, this guide will show you how to easily connect two users through one Sinch number with just a few lines of code.

number-masking-diagram

What you need to know before you start

Before you can get started, you need the following already set up:

  • Set all Voice API configuration settings.
  • ASP.NET Core 6.0 or later SDK and ASP.NET Core Runtime and a familiarity with how to create an app.
  • ngrok and a familiarity with how to start a network tunnel. You can use any method you want to make your server accessible over the internet, but we like ngrok and it's what we'll use in this guide.
  • Postman or another method of making API requests to a server on your machine. You can use whatever you like, but Postman makes things easy, and it's what we'll use in this guide.
  • Two mobile phone handsets you can use to test. This isn't strictly necessary, but you can get more of an end-to-end experience of the entire process.

Set up your ASP.NET Core 6.0 application

Create a new project folder and open a command prompt. Execute the following command to create a new ASP.NET Core 6.0 web API application:

Copy
Copied
dotnet new webapi

This creates a new web API application and project. Now add the Sinch Voice SDK. Run the following command:

Copy
Copied
dotnet add package Sinch.ServerSdk

The Voice SDK lets you use the Voice API with far fewer lines of code than you'd otherwise need.

Set up your file

In your project folder, open the Program.cs file and paste the provided "Program.cs" code into the file, replacing all the existing content.

Program.cs

This code is used to start a lightweight web API that masks the numbers of incoming calls.

using Sinch.ServerSdk;
using Sinch.ServerSdk.Calling.Models;
using Sinch.ServerSdk.Models;

var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(o =>
{
    o.SerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault;
    o.SerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
    o.SerializerOptions.PropertyNameCaseInsensitive = true;
});

var app = builder.Build();
var numberConfigs = new List<NumberConfig>();
app.MapPost("/configure", (string from, string to) => {
    numberConfigs.RemoveAll(n => n.From == from);
    var config = new NumberConfig {
        From = from,
        To = to
    };
    numberConfigs.Add(config);
    return Results.Created("/configure", config);
});

app.MapGet("/configure", () => numberConfigs);

app.MapPost("/incoming-call", (CallbackEventModel model) => {
    var sinch = SinchFactory.CreateCallbackResponseFactory(Locale.EnUs);
    var builder = sinch.CreateIceSvamletBuilder();
    if (model.Event == "ice"){
        var config = numberConfigs.FirstOrDefault();
        builder.ConnectPstn(config.To)
               .WithCli(model.To.Endpoint)
               .WithoutCallbacks();
    
    }
    return builder.Build().Model;
});
app.Run("http://localhost:3000");

public class NumberConfig
{
    public string From { get; set; }
    public string To { get; set; }
}

This code starts a lightweight web API server listening on the localhost of your machine. It also contains the logic necessary to handle the following tasks:

  • Create a configuration object that determines what number will be displayed as the "from" when a number calls.
  • View the current configuration object data.
  • Listen for callbacks from the Sinch servers that indicate a phone call is incoming and use the configuration object to mask the phone number.

We'll go through and cover each of those functions in detail, but first let's start the server and the network tunnel so we can configure the callback URL in the app on the dashboard.

Start your server

Start your server with the following command:

Copy
Copied
dotnet run

Start your ngrok tunnel

At this point you need to start your ngrok tunnel so that the Sinch servers can access the webserver running on your local machine. Run the following command to start your tunnel:

Copy
Copied
ngrok http 3000

This starts a tunnel to your webserver, which is running on port 3000 of your localhost.

Copy the http ngrok URL and add "/incoming-call" to the end of it. For example, it would look something like: http://a010-143-178-217-148.ngrok.io/incoming-call.

Navigate to your app on your dashboard. Under the Settings section, you'll see a field labeled "Callback URL." Paste your ngrok URL into that field and click Save.

Also take note of the Sinch phone number you have assigned to this app, as you'll be calling that number in another step.

With that done, now we can create a configuration object to hold the phone number we want to mask.

Create a number configuration object

In a production application, likely you'd use some kind of database to keep track of phone numbers. But for the purposes of this sample we're going to simplify things. Essentially, all we need is a record of what number to use as the caller ID when another number calls, and which number you want to call when your Sinch number is dialed. You could use a key value pair in a collection, but we created a simple NumberConfig class with a From and a To property. The From property is the number that you want to use as the caller ID, and the To property is the number you will be calling.

To create this configuration object, you'll be making a POST request to the server running on your local host, with the numbers you want to use in your query. Using Postman, make the following POST request:

Copy
Copied
http://localhost:3000/configure?from=<from-phone-number>&to=<to-phone-number>

Replace <from-phone-number> with the number you'll be calling from and replace <to-phone-number> with the number you want to call. Make sure the numbers you're using are in E.164 format with the country code and +.

Tip:

If you're using an Apple computer, you may need to replace the + with the escaped URL %2B.

You can test if this configuration was saved by navigating to http://localhost:3000/configure in your web browser.

Mask a phone call

Now you can test and make sure everything is working. Using the phone number you assigned to the From property, call your Sinch phone number. This will cause an Incoming Call Event callback to be sent by the Sinch servers to your local server. Your local server will then respond to this Incoming Call Event by making a call to the number you assigned to the To property. You should now have a phone call between your two handsets, with your Sinch phone number as the caller ID, effectively masking the phone number you called from.

Next steps

Was this page helpful?

Program.cs

This code is used to start a lightweight web API that masks the numbers of incoming calls.

using Sinch.ServerSdk;
using Sinch.ServerSdk.Calling.Models;
using Sinch.ServerSdk.Models;

var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(o =>
{
    o.SerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault;
    o.SerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
    o.SerializerOptions.PropertyNameCaseInsensitive = true;
});

var app = builder.Build();
var numberConfigs = new List<NumberConfig>();
app.MapPost("/configure", (string from, string to) => {
    numberConfigs.RemoveAll(n => n.From == from);
    var config = new NumberConfig {
        From = from,
        To = to
    };
    numberConfigs.Add(config);
    return Results.Created("/configure", config);
});

app.MapGet("/configure", () => numberConfigs);

app.MapPost("/incoming-call", (CallbackEventModel model) => {
    var sinch = SinchFactory.CreateCallbackResponseFactory(Locale.EnUs);
    var builder = sinch.CreateIceSvamletBuilder();
    if (model.Event == "ice"){
        var config = numberConfigs.FirstOrDefault();
        builder.ConnectPstn(config.To)
               .WithCli(model.To.Endpoint)
               .WithoutCallbacks();
    
    }
    return builder.Build().Model;
});
app.Run("http://localhost:3000");

public class NumberConfig
{
    public string From { get; set; }
    public string To { get; set; }
}