How to Manage Your Twilio Verify Safe List in Laravel

October 17, 2025
Written by
Reviewed by

How to Manage Your Twilio Verify Safe List in Laravel

Twilio's Verify Fraud Guard and Geo Permissions are two excellent ways Twilio helps protect your Verify Service from suspicious activity like SMS Pumping Fraud as it automatically blocks risky traffic.

Sometimes, however, they can also block safe phone numbers — such as your own — preventing you from contacting your customers.

To solve this problem, Twilio introduced the Safe List API. It allows you to maintain a list of phone numbers that will never be blocked by Verify Fraud Guard or Geo permissions.

In this short tutorial, you'll learn how to integrate the Safe List API into Laravel so that you can add approved phone numbers to, view existing numbers on, and remove phone numbers from your Safe List as necessary.

Tutorial prerequisites

To follow along with the tutorial, you will need the following:

  • PHP 8.3 or above
  • A Twilio account (free or paid). Create a new account if you don't already have one.
  • The Laravel Installer
  • Your preferred PHP editor or IDE, such as NeoVIM or PhpStorm
  • Curl or your network testing tool of choice
  • A little bit of command line experience

Create a new Laravel project

The first thing we need to do is scaffold a new Laravel project, change into the project directory, and enable API routing. We'll do that by running the following three commands.

laravel new safe-list-laravel-integration
cd safe-list-laravel-integration
php artisan install:api

When prompted, accept the default answers to the setup questions.

Add the required dependencies

The next thing to do is to add the dependencies that PHP requires. Gladly, there's only one: Twilio's Helper Library for PHP. To install it, run the command below.

composer require twilio/sdk

Set the required environment variables

Now, we need to define two environment variables, necessary for making authenticated requests to Twilio. These are your Twilio Account SID and Auth token.

First, add the following to the end of .env.

TWILIO_ACCOUNT_SID=<TWILIO_ACCOUNT_SID>
TWILIO_AUTH_TOKEN=<TWILIO_AUTH_TOKEN>

Then, log in to your Twilio Console and, from the Account Info panel, copy your Account SID and Auth Token. Then, paste them into .env in place of <TWILIO_ACCOUNT_SID> and <TWILIO_AUTH_TOKEN>, respectively.

Screenshot

Finally, we need to make the configuration available in Laravel. To do that, create a new file in the config directory named twilio.php, and paste the code below into the file.

<?php

return [
    'account_sid' => env('TWILIO_ACCOUNT_SID', false),
    'auth_token' => env('TWILIO_AUTH_TOKEN', false),
];

Create a service for interacting with Twilio

The next thing we need to do is create a service class to store the logic for interacting with Twilio's Safe List API. Start off by creating a directory named Service in the app directory. Then, in that directory create a new file named TwilioSafeListService.php. With that done, add the following code to the new file.

<?php

namespace App\Service;

use Twilio\Rest\Client;
use Twilio\Rest\Verify\V2\SafelistInstance;

readonly final class TwilioSafeListService
{
    public function __construct(private Client $client) {}

    public function addNumber(string $phoneNumber): SafelistInstance
    {
        return $this->client
            ->verify
            ->v2
            ->safelist
            ->create($phoneNumber);
    }

    public function checkNumber(string $phoneNumber): SafelistInstance
    {
        return $this->client
            ->verify
            ->v2
            ->safelist($phoneNumber)
            ->fetch();
    }

    public function removeNumber(string $phoneNumber): bool
    {
        return $this->client->verify
            ->v2
            ->safelist($phoneNumber)
            ->delete();
    }
}

The class is instantiated with a Twilio\Rest\Client instance. If you're not familiar with this class, it super-simplifies interacting with Twilio's Safe List API (and every other Twilio API), providing fluent methods for managing phone numbers on the list. We'll register an object instance with Laravel's Service Container in the next section.

After the class constructor, three functions are defined:

  • addNumber: This function adds the phone number passed to the function to the user's Safe List, and returns a SafeListInstance containing the full details of the item in the user's Safe List.
  • checkNumber: This function checks if a phone number is on the user's Twilio Safe List and returns the entry's details if it is.
  • removeNumber: This function removes the phone number passed to the function from the user's Safe List, and returns true or false showing if the operation succeeded or not.

Create an register a Service Provider

The next thing that we need to do is to register a Twilio\Rest\Client instance with Laravel's Service Container so that when a TwilioSafeListService instance is requested, it can be instantiated correctly.

To do that, we'll use a custom Service Provider. Create it by running the following command.

php artisan make:provider TwilioServiceProvider

The command creates a file named TwilioServiceProvider.php in the app/Providers directory. Open the file in your editor or IDE and replace the file's content with the code below.

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\ServiceProvider;
use Twilio\Rest\Client;

class TwilioServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     */
    public function register(): void
    {
        $this->app->singleton(
            Client::class,
            fn ($app) => new Client(
                Config::get('twilio.account_sid'),
                Config::get('twilio.auth_token'),
            )
        );
    }

    /**
     * Bootstrap services.
     */
    public function boot(): void
    {
        //
    }
}

The register() function calls app::singleton() to register a service named Twilio\Rest\Client. When the service is requested, it returns a new Twilio\Rest\Client object instantiated with your Twilio Account SID and Auth Token, which you defined earlier, at the end of .env.

Create a controller

Now it's time to create a controller to store the application's business logic. To do that, run the command below.

php artisan make:controller TwilioSafeListController

The command creates a file named TwilioSafeListController.php in the app/Http/Controllers directory. Open the file in your editor or IDE and replace its contents with the following code.

<?php

namespace App\Http\Controllers;

use App\Service\TwilioSafeListService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Twilio\Exceptions\TwilioException;

final class TwilioSafeListController extends Controller
{
    public const string REGEX_E164 ='^\+[1-9]\d{1,14}$';

    public function __construct(private readonly TwilioSafeListService $service)
    {
    }

    public function add(Request $request): JsonResponse
    {
        $validated = $request->validate([
            "phone_number" => sprintf("required|regex:/%s/", self::REGEX_E164),
        ]);
        try {
            $safeList = $this->service->addNumber($validated["phone_number"]);
            return response()->json([
                "{$validated["phone_number"]} was successfully added to your Twilio Safe List. Its Safe List ID is {$safeList->sid}.",
            ]);
        } catch (TwilioException $e) {
            return response()->json([
                'error' => $e->getMessage(),
                'error_code' => $e->getCode(),
            ]);
        }
    }

    public function remove(string $phoneNumber): JsonResponse
    {
        try {
            return ($this->service->removeNumber($phoneNumber))
                ? response()->json([
                    "{$phoneNumber} was successfully removed from Twilio Safe List.",
                ])
                : response()->json([
                    "{$phoneNumber} was NOT removed from Twilio Safe List.",
                ]);
        } catch (TwilioException $e) {
            return response()->json([
                'error' => $e->getMessage(),
                'error_code' => $e->getCode(),
            ]);
        }
    }

    public function check(string $phoneNumber): JsonResponse
    {
        try {
            $safeList = $this->service->checkNumber($phoneNumber);
        } catch (TwilioException $e) {
            return response()->json([
                'error' => $e->getMessage(),
                'error_code' => $e->getCode(),
            ]);
        }
        return ($safeList->sid === null)
            ? response()->json([
                "{$phoneNumber} is NOT on your Twilio Safe List.",
            ])
            : response()->json([
                "{$phoneNumber} is on your Twilio Safe List. Its Safe List ID is {$safeList->sid}.",
            ]);
    }
}

The controller starts by defining a regex (regular expression) for validating that a phone number is in E.164 format.

What is the E.164 format? Quoting Twilio's documentation:

E.164 is the international telephone numbering plan that ensures each device on the Public Switched Telephone Network (PSTN) has a globally unique number. This numbering plan allows phone calls and text messages to be correctly routed to individual phones in different countries. E.164 numbers are formatted as + country code subscriber number including area code and can have a maximum of fifteen digits.

The class then defines three functions, backing the application's three API endpoints:

  • add: This function starts by validating that the request body has an element named phone_number which contains a phone number in E.164 format. If so, it passes the phone number to a call to TwilioSafeListService's addNumber() function. If successful, a JSON response is returned telling the user that the phone number was added to their Safe List. If not, a JSON response containing the why the phone number was not added and the accompanying error code is returned.
  • remove: This function accepts a string, which is the phone number to remove from the user's Safe List, passing it to TwilioSafeListService's removeNumber() function. Based on if the function returns true or not, remove() returns a JSON response telling the user if the phone number was removed from their Safe List or not. If an exception is thrown, a JSON response containing the why the phone number was not added and the accompanying error code is returned.
  • check: Similar to the remove() function, this function accepts a string, which is the phone number to look for in the user's Safe List. It passes the number to a call to TwilioSafeListService's checkNumber() function. Based on the function's response, it returns a JSON response telling the user if the phone number was on their Safe List or not. If an exception is thrown, a JSON response containing the why the phone number was not added and the accompanying error code is returned.

Update the routing table

Now that all of the core code's been written, we'll finish up by adding the required routes to the application's routing table. To do that, add the following to the end of routes/api.php.

Route::get('/safelist/{phoneNumber}', [TwilioSafelistController::class, 'check'])
    ->where([
        'phoneNumber' => TwilioSafelistController::REGEX_E164,
    ]);
Route::post('/safelist', [TwilioSafelistController::class, 'add']);
Route::delete('/safelist/{phoneNumber}', [TwilioSafelistController::class, 'remove'])
    ->where([
        'phoneNumber' => TwilioSafelistController::REGEX_E164,
    ]);

Then, add the following use statement at the top of the file:

use App\Http\Controllers\TwilioSafeListController;

The first route checks if a phone number is on the user's Safe List. Accepting only the GET method, when dispatched, TwilioSafelistController's check() function will be called, so long as the phone number provided (in the phoneNumber route parameter) matches the regular expression defined in TwilioSafelistController::REGEX_E164.

The second route, accepting only the POST method, calls TwilioSafelistController's check() function to add a phone number to the user's Safe List.

The third route, accepting only the DELETE method, calls TwilioSafelistController's remove() function to delete the phone number provided (in the phoneNumber route parameter) from the user's Safe List, so long as the phone number matches the regular expression defined in TwilioSafelistController::REGEX_E164.

Test that the code works as expected

With everything in place, we now need to check that the code works properly. Before we can do that, however, we need to start the application by running the following command.

php artisan serve

Now, in a new terminal tab or session, let's check if a phone number is in the Safe List. Replace <Your Phone Number> in the command below, then run it.

curl --silent "http://localhost:8000/api/safelist/<Your Phone Number>"

If the phone number is in your Safe List, you'll see output similar to the JSON below (formatted for greater readability).

[
  "<Your Phone Number> is on your Twilio Safe List. Its Safe List ID is GNe4ea5be79f384111111c014812a44ee1."
]

If the phone number isn't on your Safe List, you'll see output similar to the JSON below:

{
  "error": "[HTTP 404] Unable to fetch record: The requested resource /v2/SafeList/Numbers/<Your Phone Number> was not found",
  "error_code": 20404
}

Next, let's add a phone number to the Safe List. Replace <Your Phone Number> in the command below, then run it.

curl \
  --silent \
  --data-urlencode "phone_number=<Your Phone Number>" \
  http://localhost:8000/api/safelist

If the phone number was successfully added, you should see output similar to the JSON below, printed to the terminal.

[
  "<Your Phone Number> was successfully added to your Twilio Safe List. Its Safe List ID is GNe4ea5be79f384224832c028414b76cc1."
]

Finally, let's remove a phone number from the Safe List. As before, replace <Your Phone Number> in the command below, then run it.

curl \
  --silent \
  --request DELETE \
  "http://localhost:8000/api/safelist/<Your Phone Number>"

You'll see output similar to the following if the phone number was successfully removed from your Safe List.

[
  "<Your Phone Number> was successfully removed from Twilio Safe List."
]

That's how to use Twilio's Verify Safe List in Laravel

Now you know how to maintain a list of phone numbers in Laravel that will never be blocked by Verify Fraud Guard and Geo permissions by using Twilio's Safe List. I hope that you make use of it in your Laravel applications to help ensure that they're as secure as possible.

Happy building!

Matthew Setter is (primarily) the PHP, Go, and Rust editor on the Twilio Voices team. He’s also the author of Mezzio Essentials and Deploy with Docker Compose. You can find him at msetter[at]twilio.com. He's also on LinkedIn and GitHub.

Requirement icons created by Iconjam on Flaticon.