How to confirm phone number ownership with Lookup Identity Match

May 30, 2024
Written by

New in 2024: Identity Match is self-service in the United States.

For countries outside the US, our carrier partners require approval prior to use. Submit this form for approval; expected turnaround is 2-4 weeks. Learn more about the carrier approval process here.

There's a lot you can learn from a phone number. Lookup Identity Match uses phone numbers as an identity to provide a zero-knowledge match against authoritative data sources like mobile carrier records. This allows you to build real-time confirmation that you're interacting with a real person linked to the phone number provided.

Diagram showing the inputs and outputs of the Lookup Identity Match

Use Identity Match to prevent fake accounts and improve trust with legitimate, unique users. Customers can use Identity Match at sign up, login, and even during account changes or checkout. Best of all, Identity Match happens seamlessly in the background, with lower costs and friction than alternatives like credit bureau and document verification used to satisfy Anti-Money Laundering (AML) rules.

This blog post will show you how to use Twilio Lookup Identity Match to confirm phone number ownership and reduce sign up fraud.

Here's a sneak peak at what the Identity Match request looks like. Learn more about accepted parameters and coverage in the documentation, plus how to test different outcomes with our "magic numbers" and special parameters.

# use test credentials found here https://console.twilio.com/us1/account/keys-credentials/api-keys
export TWILIO_TEST_CRED_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export TWILIO_TEST_CRED_AUTH_TOKEN=your_auth_token

curl -X GET "https://lookups.twilio.com/v2/PhoneNumbers/+12345678900\
?Fields=identity_match\
&FirstName=John\
&LastName=Twilion\
&AddressLine1=123+Magic+St\
&City=Owl+City\
&State=CA\
&PostalCode=12345\
&AddressCountryCode=US\
&DateOfBirth=20080313\
&NationalId=123456789" \
-u $TWILIO_TEST_CRED_ACCOUNT_SID:$TWILIO_TEST_CRED_AUTH_TOKEN

# returns:
# {
#   ...
#   "identity_match": {
#     "address_country_match": "exact_match",
#     "address_lines_match": "exact_match",
#     "city_match": "exact_match",
#     "date_of_birth_match": "exact_match",
#     "error_code": null,
#     "error_message": null,
#     "first_name_match": "exact_match",
#     "last_name_match": "exact_match",
#     "national_id_match": "exact_match",
#     "postal_code_match": "exact_match",
#     "state_match": "exact_match",
#     "summary_score": 100
#   },
#   "national_format": "(234) 567-8900",
#   "phone_number": "+12345678900",
#   "valid": true,
#   ...
# }

Prerequisites for building with Lookup Identity Match

To code along with this post you'll need:

  1. A Twilio account. Sign up or sign in.
  2. [Outside US only] Submit this form for Identity Match approval (takes 2-4 weeks)
  3. Twilio CLI (I'm using v5.5.0)
  4. CLI Serverless plugin (run twilio plugins:install @twilio-labs/plugin-serverless)

This post uses the international telephone input Code Exchange project as a starting point, but you could use your own sign up form instead. The base project relies on the excellent intl-tel-input plugin for collecting phone numbers in E.164 format.

Start by cloning the example phone number input using the Twilio CLI:

twilio serverless:init identity-match --template=international-telephone-input && cd identity-match

Add your Auth Token in the created .env file within the identity-match folder. Test out the project by running twilio serverless:start, you should be able to open http://localhost:3000/index.html and see a phone number input form.

Extend your sign up form with Identity Match

Identity match needs information about the phone number owner to match against. Parameters can be any combination of name, address, national ID or date of birth.

This example is going to use first and last name, so you need to start by making sure your form has fields to collect those separately:

Open assets/index.html and replace everything inside the <form> tags with:

<div>
  <label for="firstName">First name:</label>
  <input id="firstName" type="text" name="firstName">
</div>
<div>
  <label for="lastName">Last name:</label>
  <input id="lastName" type="text" name="lastName">
</div>
<div>
  <label for="phone">Phone number:</label>
  <input id="phone" type="tel" name="phone">
</div>
<input type="submit" class="btn" value="Verify" />

This adds a first name and last name input in addition to the phone number input.

Further down in index.html in the script section, add the following code under const data = … to extract the name input values.

data.append("firstName", document.getElementById("firstName").value);
data.append("lastName", document.getElementById("lastName").value);

Then head over to functions/lookup.js to make use of the data and call the Lookup Identity Match API. The existing code already calls the basic Lookup API for formatting and validation, so you need to add the additional data parameters to tell the API to use the Identity Match package.

Inside the try block but before const client, create a data object:

const data = {
  fields: "identity_match",
  firstName: event.firstName,
  lastName: event.lastName,
};

Then add data as a parameter to the the existing Lookup fetch like so:

const lookup = await client.lookups.v2
  .phoneNumbers(event.phone)
  .fetch(data);

How to interpret the results of a Lookup Identity Match response

After calling the Lookup API, you need to decide what to do with the information in the response. Here's what the identity match portion of a sample response looks like:

{
  "first_name_match": "exact_match",
  "last_name_match": "high_partial_match",
  "address_lines_match": "no_match",
  "city_match": "no_match",
  "state_match": "high_partial_match",
  "postal_code_match": "no_data_available",
  "address_country_match": "exact_match",
  "national_id_match": null,
  "date_of_birth_match": "exact_match",
  "summary_score": 90,
  "error_code": null,
  "error_message": null
},

Possible responses include exact_match, no_match, and no_data_available. Name and address fields also support partial matches. Learn more about match scores in the documentation. The API will respond null if you don't provide an input to match on.

This application is only looking at name matches and will consider the match a success if it's an exact match or high partial match (e.g. Kelly instead of Kelley – happens all the time).

Back in lookup.js, replace const success = … with the following:

const fnm = lookup.identityMatch.first_name_match;
const lnm = lookup.identityMatch.last_name_match;
const success =
  lookup.valid &&
  (fnm == "exact_match" || fnm == "high_partial_match") &&
  (lnm == "exact_match" || lnm == "high_partial_match");

const respBody = {
  success,
  msg: `First name is ${fnm}. Last name is ${lnm}`,
};

Then replace the .setBody call inside the if (success) block with:

response.setBody(respBody);

Finally, make two small changes to display the response message in index.html. Replace the inside of the if (json.success) { block with:

info.style.display = "";
info.innerHTML = json.msg;

And since you're no longer only checking the phone number, update the error message in the else block a few lines below:

error.innerHTML = "Cannot register with the information provided. Please ensure you're using a phone number you own."

Launch the application with twilio serverless:start and test it out!

sign up-like form with a slightly mis-spelled first name that returns a high partial match

Next steps for identity verification

Try out different combinations with both real data and the test parameters to see the results.

You can extend your application with other Lookup packages like Line Type Intelligence, SIM swap detection and more. Check out our documentation to learn more about what's possible with the Lookup API.

Another way to prevent invalid phone numbers is to do phone verification - we also have an API for that! I can't wait to see what you build and secure.