Scheduling SMS in Laravel with Twilio

October 07, 2025
Written by
Lucky Opuama
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Scheduling SMS in Laravel with Twilio

It's easy to forget important appointments, messages, and manual reminders. This can lead to missed opportunities or deadlines. To solve this, automate the skittleing of SMS notifications with Twilio Programmable Messaging API and Laravel, ensuring timely reminders with a minimum of manual effort.

In this tutorial, you will learn how to do just this.

Prerequisites

To follow through with this tutorial, ensure you have:

Create a new Laravel project

Run the following command to create a new Laravel project, and change into its directory:

composer create-project laravel/laravel sms-scheduler 
cd sms-scheduler

Once your application has been created, you can start it by running the command below in your terminal:

php artisan serve

The Laravel app should now be available at http://127.0.0.1:8000. Open this URL in your browser, and you should see a page similar to the one shown below.

Laravel homepage with links to documentation and Laracasts tutorials and a Deploy now button.

Install Twilio's PHP Helper Library

Twilio's PHP Helper Library is a powerful package that provides convenient methods for interacting with Twilio's APIs to integrate services like SMS, phone calls, and more into your Laravel applications. To install this package, run the command below in a new terminal session:

composer require twilio/sdk

Retrieve the required environment variables

The first thing to do is to add the following configuration at the end of your .env file. These will set up the four environment variables you'll need to interact with Twilio's Programmable Messaging API.

TWILIO_SID=<<your_twilio_sid>>
TWILIO_AUTH_TOKEN=<<your_twilio_auth_token>>
TWILIO_PHONE_NUMBER=<<your_twilio_phone_number>>
TWILIO_MESSAGING_SERVICE_SID=<<your_messaging_service_sid>>

Specifically, for this application, you'll need the following Twilio credentials: Account SID, Auth Token, Twilio phone number,and a Twilio Messaging Service SID. To retrieve the first three, log in to your Twilio Console dashboard, where you'll find your Account SID, Auth Token, and Twilio phone number displayed under the Account Info section.

Screenshot of Twilio account info showing account SID, masked auth token, and phone number.

Copy them and paste them into .env in place of <<your_twilio_sid>>, <<your_twilio_auth_token>>, and <<your_twilio_phone_number>>, respectively.

Unless you have one already, you'll next need to create a Messaging Service SID in the Messaging Services section of your Twilio dashboard. To create a new Messaging Service, go to Messaging > Services and select the Create Messaging Service button.

Screenshot of a messaging services dashboard with search and create messaging service options.

Next, complete the first step of the Messaging Service Setup by entering a Messaging Service friendly name. Leave the purpose set to the default option, "Notify my users", and proceed to the next step.

Screenshot of the Messaging Service Setup interface step 1 to create a new messaging service.

The Sender Pool is the next stage, in which you provide your Twilio registered number. Click on the Add Senders button. A popup page will open on your screen; select "Phone Number" as the Sender Type and click Continue.

Screenshot of Twilio interface for adding senders by phone number with step-by-step guide.

Next, select the checkbox for the phone number you want to use as the sender, then click "Add Phone Numbers" and click Confirm to confirm the sender association. After that, click the Step 3: Set Up Integration button to proceed to the next step.

Interface for adding senders' phone numbers, showing a phone number, capabilities, and messaging service selection.

Proceed straight to Step 4, by clicking Step 4: add compliance info, as the integrations are already set. At this point, you will see a confirmation window that says, "You have successfully updated your Messaging Service". Finally, click on Complete Messaging Service Setup to finalize the process.Your messaging service is now fully configured!

Now, let's run some tests to see if the messaging service works. Click the Try sending a message button on the popup page that appears on your screen.

Twilio interface showing confirmation of setting up a new messaging service with options for viewing and sending a message.

Next, select "Send to Personal Number" and enter your verified Twilio number as the recipient. Choose "Messaging Service" as the sender type and select the service name you created. Finally, type any message in the message textbox and click the "Send an SMS" button.

Screenshot of Twilio dashboard showing SMS messaging service setup and API request code snippet.

Twilio will send an SMS containing the content of your message to the specified recipient to ensure that your messaging service is operational.

Now, go back to Messaging > Services and copy the Message Service SID, located in the Sid column of the messaging service you just created. Then, paste it into .env in place of <<your_messaging_service_sid>>.

Create the database and model

Now, let's establish a structured way to store SMS messages and their scheduled sending timings. Here is where the application's database comes in handy. It will include vital information such as the recipient's phone number, message content, planned time, and delivery status.

We'll now create a model to represent the database table, allowing interaction with it using Eloquent ORM, and enabling easy data retrieval, insertion, and updates without writing raw SQL queries, and a migration file to create the table in the database.

To create both files, run the following command in your terminal:

php artisan make:model ScheduledSms -m

Once the command is executed, navigate to the database/migrations directory. You will find a newly generated migration file, named with a timestamp indicating the current date and time it was created and ending with _create_scheduled_sms_table.php. Open this file and replace its contents with the following code:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up()
    {
        Schema::create('scheduled_sms', function (Blueprint $table) {
            $table->id();
            $table->string('recipient');
            $table->text('message');
            $table->dateTime('send_at');
            $table->enum('status', ['pending', 'sent', 'failed'])->default('pending');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('scheduled_sms');
    }
};

This migration creates a scheduled_sms table with the following fields:

  • id: A Unique identifier (Primary Key)
  • recipient: Phone number of the recipient
  • message: SMS content
  • send_at: Date and time to send the SMS
  • status: SMS delivery status (pending, sent, failed)
  • timestamps: Timestamp columns for record tracking

Next, use the command below to execute the database migrations you just created.

php artisan migrate

Now, navigate to the app/Models directory and open the ScheduledSms.php model file. Update the file with the following code:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class ScheduledSms extends Model
{
    use HasFactory;

    protected $fillable = ['recipient', 'message', 'send_at', 'status'];
    protected $casts = [
        'send_at' => 'datetime',
    ];
}

To improve security and prevent unauthorized data updates, the $fillable array is used. It specifies which columns can be mass-assigned, hence eliminating Mass Assignment hazards, allowing only particular fields to be updated or altered. While the $casts array automatically converts send_at to a Carbon date-time instance, allowing for easy date manipulation and formatting.

Create the Schedule SMS controller

To create the scheduled SMS controller, containing the application's core logic, run the command below:

php artisan make:controller ScheduledSmsController

After that, navigate to the ScheduleSms.php file located in the app/Http/Controllers directory and replace its contents with the following code:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\ScheduledSms;
use Carbon\Carbon;

class ScheduledSmsController extends Controller
{
    public function scheduleSms(Request $request)
    {
        $request->validate([
            'recipient' => 'required|string',
            'message' => 'required|string',
            'send_at' => 'required|date|after:now',
        ]);

        ScheduledSms::create([
            'recipient' => $request->recipient,
            'message' => $request->message,
            'send_at' => Carbon::parse($request->send_at),
            'status' => 'pending',
        ]);

        return response()->json(['message' => 'SMS scheduled successfully!']);
    }
}

The code above validates the incoming request, ensuring that only properly formatted and valid data is processed. It also securely stores the scheduled SMS details in the database.

Create the SMS scheduling command

The SMS scheduling command automates message delivery by sending scheduled SMS at the right time. Without relying on third-party cron job services, Laravel's scheduler efficiently manages recurring tasks, ensuring that messages are sent on time and without manual intervention.

To create the SMS scheduling command, run the following command:

php artisan make:command SendScheduledSms

Next, navigate to the app/Console/Commands directory, open the SendScheduledSms.php file and update it to match the following code.

<?php

namespace App\Console\Commands;

use App\Models\ScheduledSms;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Twilio\Rest\Client;

class SendScheduledSms extends Command
{
    protected $signature = 'sms:send-scheduled';
    protected $description = 'Send scheduled SMS messages';

    public function handle()
    {
        $smsList = ScheduledSms::where('status', 'pending')
            ->where('send_at', '<=', Carbon::now())
            ->get();
        if ($smsList->isEmpty()) {
            $this->info('No scheduled SMS to send.');
            return;
        }

        $twilio = new Client(env('TWILIO_SID'), env('TWILIO_AUTH_TOKEN'));
        foreach ($smsList as $sms) {
            try {
                $twilio->messages->create(
                    $sms->recipient,
                    [
                        'from' => env('TWILIO_PHONE_NUMBER'),
                        'body' => $sms->message,
                        'messagingServiceSid' => env('TWILIO_MESSAGING_SERVICE_SID'),
                    ]
                );
                $sms->update(['status' => 'sent']);
                $this->info("SMS sent to: {$sms->recipient}");
            } catch (\Exception $e) {
                $sms->update(['status' => 'failed']);
                $this->error("Failed to send SMS to {$sms->recipient}: " . $e->getMessage());
            }
        }
    }
}

The handle() method retrieves pending SMS messages from the database, verifies if they are due for sending, dispatches them via Twilio's API, and updates their status to track successful and failed deliveries.

Next, navigate to your routes directory and update the code in the console.php file with the following code:

<?php

use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Console\Scheduling\Schedule;

Artisan::command('inspire', function () {
    $this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote');
app()->booted(function () {
    $schedule = app(Schedule::class);
    $schedule->command('sms:send-scheduled')->everyMinute();
});

This code registers a scheduled task, ensuring that the sms:send-scheduled command runs every minute to process and send scheduled SMS messages.

Add the route

For routing, we will be using API routes because the SMS scheduling system interacts with Twilio's APIs, making it a stateless API-driven process.

Run the following command to set up the API routing table:

php artisan install:api

If you're prompted with the following prompt, answer "Yes".

One new database migration has been published. Would you like to run all pending database migrations? (yes/no) [yes]

Next, navigate to the routes directory and open the api.php file. Then, add the following route:

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ScheduledSmsController;

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');
Route::post('/schedule-sms', [ScheduledSmsController::class, 'scheduleSms']);

This route allows you to schedule an SMS by sending a POST request.

Testing the application

Now, let’s test the scheduler and see if it works. Ensure that your Laravel application is running, if not, run this command

php artisan serve

Then run the following command on your terminal:

php artisan schedule:work

This command runs in the foreground and triggers the scheduler every minute, continuing until manually terminated. If tasks are scheduled to run at sub-minute intervals, the scheduler will remain active within each minute to ensure those tasks are executed as expected.

Next, open another terminal and run this command:

php artisan schedule:run

To schedule an SMS, follow these steps to send a POST request to http://127.0.0.1:8000/api/schedule-sms using Postman:

  1. Open Postman.
  2. Set request type to POST.
  3. Enter URL: http://127.0.0.1:8000/api/schedule-sms
  4. Click on the "Body" tab, select "raw", set format to "JSON".
  5. Add the JSON data below:
{
    "Recipient": "<your phone number>",
    "Message": "<your message>",
    "Sent_at": "<current date and time>"
}

Replace the placeholders with your actual data, as demonstrated in the screenshot below.

Post request to schedule an SMS with recipient, message, and send_at parameters in JSON format.

Then click the Send button.

Running this API call stores the SMS message in your database with the scheduled send time. Laravel’s task scheduler will then check the database and send the SMS when the scheduled time arrives.

That's how to schedule SMS in Laravel with Twilio

Scheduling SMS in Laravel with Twilio is a powerful way to automate messaging for reminders, notifications, and even marketing campaigns. In this tutorial, we utilize Laravel's built-in task scheduling and Twilio’s API and messaging services. Now you can efficiently send messages at your predefined time without manual intervention.

Lucky Opuama is a software engineer and technical writer with a passion for exploring new tech stacks and writing about them. Connect with him on LinkedIn.

SMS icon created by Eucalyp, and the calendar icon was created on Flaticon.