DEV Community

Cover image for Sending Scheduled and Recurring Email Notifications with PHP
Oleksii Klochai for Courier

Posted on • Originally published at courier.com

Sending Scheduled and Recurring Email Notifications with PHP

Sending timely targeted email notifications greatly affects how your audience engages with your product. For these notifications to be effective at notifying your users of key events, you need to schedule when they are sent so that they are delivered at the right time.

Scheduled and recurring notifications are in use everywhere — for example, online stores use scheduled notifications to inform users about sale events (like Black Friday), and doctors, dentists, and tradespeople have systems that send appointment reminders. Recurring emails are commonly used for subscription services to email monthly bills to customers.

This tutorial covers two different ways for PHP developers to send scheduled and recurring email notifications through the Courier notification platform using its PHP SDK. It also offers a low-code solution for sending scheduled emails using just the Courier UI. Courier is a multi-channel notification service with a robust API, which you can use to build a production-ready email notification system in a few minutes.

Configure an email service provider in Courier

Create a Courier account if you don’t already have one, so that you can configure an email service provider in Courier, allowing it to send emails on your behalf. \

In the Courier app, navigate to Channels and choose your email service provider. For this tutorial, we will use Gmail.

Screenshot showing how to select an email service provider in Courier.

On the next screen, select Sign in with Google to give Courier permission to access your Gmail account.

Create a notification template in Courier

Courier uses email templates to make it easy to reuse your emails. In this tutorial, we will use an example of a hairdressing business that sends emails to its clients before (and sometimes after) appointments.

To create your first template, start by navigating to the Designer. Click Create Template, give it the name Hair appointment reminder, and click Create Template again.

Animation showing how to create a notification template in Courier.

Next, select Email as the channel for this notification (choosing Gmail as the specific provider in the drop-down box). Now, click on your new email channel on the left side to see the no-code editor for designing your notification.

Animation showing how to add text to an email template in Courier and choose Gmail as your notification provider.

Give your notification the subject “Hair appointment reminder”, and paste the following content into the message body:

Dear {name},

This email is to confirm your upcoming hair appointment tomorrow.

We look forward to seeing you at {place}. Please arrive five minutes before your appointment start time.

If you need to reschedule, please let us know at least 24 hours prior to your appointment time.

Thank you for your appointment.

The {salonName} Team
Enter fullscreen mode Exit fullscreen mode

The curly braces in this template are variables that will later be passed to the template as data. Now that your template is complete, click Publish Changes. Later on, you will need your notification template ID and Courier authorization token when you want to call the Courier API. These are both available in your notification template settings. Navigate there now and copy both so that you can use them later.

Animation showing how to copy the notification template ID and Courier authorization token in your notification settings.

Now you have three options:

  1. Directly send scheduled or recurring emails using Courier*:* Call the sendEnhancedNotification() function from the Courier PHP SDK, and use a third party task scheduling library called Crunz to deal with the scheduling side of things. This works using cron syntax, so the same principle can be used for scheduled or recurring emails.
  2. Use Courier’s automations to add send logic to your scheduled emails: An automation in Courier is a way of chaining together different steps such as the sending of emails (or other notification-related logic) so that the steps happen in a particular order. An automation can be run by calling the invokeAutomation()function, and as with option 1, you can use Crunz to deal with the scheduling.
  3. Using Courier’s no-code automations designer: This is a no-code GUI tool in the Courier UI that uses a drag-and-drop canvas to build up your notification logic. It contains some more advanced logic than option 2 (such as the ability to create email digests or batching).

Option 1: directly send a scheduled or recurring email using Courier

Preparing your PHP environment

For both of the PHP examples shown below, you will need to prepare a PHP environment and create a notification template in the Courier app.

  1. Install the latest versions of PHP and Composer.
  2. Install the following packages using Composer:

You can install these by running the following command:

composer require crunzphp/crunz vlucas/phpdotenv trycourier/courier guzzlehttp/guzzle
Enter fullscreen mode Exit fullscreen mode

You can also find the code shown in this tutorial in our working example repository on GitHub. If you are cloning the repository, you will need to run the composer install command to install the dependencies.

Sending a scheduled email

To manage the scheduling side of things, we will use Crunz — a PHP package that allows you to run scheduled tasks within your PHP code without having to create a cron job for each of them. We will wrap this around Courier’s sendEnhancedNotification() function, which is used for sending emails.

For this example, we have specified that the hair appointment reminder should be sent at a specific time (13:30 2023-07-01). However, note that Crunz’s on() function accepts any date format parsed by PHP's strtotime() function, so it’s easy to pass in a time value derived from one of your variables.

As per the example in our GitHub repository, you must provide your configuration in an .env file at the project root. You can create this file by copying the provided example file into your project directory:

cp .env.example .env
Enter fullscreen mode Exit fullscreen mode

Then, fill out the contents of the newly copied .env file:

COURIER_AUTHORIZATION_TOKEN Courier API key, found in Settings
TEST_EMAIL_TO Use your own email address as the value
TEST_DIRECT_SEND_NOTIFICATION_TEMPLATE_ID The notification template ID for the “Hair appointment reminder” email template

Crunz requires you to add a single crontab entry that runs every minute. Whenever this job runs, any tasks scheduled using Crunz for that time will be executed. You only need to do this once for your project. Append the following line to your own user’s crontab on your PHP server (you can do this by typing crontab -e in your terminal):

* * * * * cd /path/to/project && vendor/bin/crunz schedule:run
Enter fullscreen mode Exit fullscreen mode

Replace /path/to/project with the absolute path to your PHP project. For security reasons, you should not add this line to your root user's crontab but instead create a user who has execute permissions for the project directory or is a member of the group that your web server runs under (by default, www-data on most Linux systems).

Crunz requires the presence of a configuration file, which contains a configured timezone. Create this by running this command in your project directory:

vendor/bin/crunz publish:config
Enter fullscreen mode Exit fullscreen mode

Note that the default configured timezone is UTC, but you can change this in the config if needed.

All Crunz tasks must be contained in a directory called tasks at the root level of your project. The file containing each task should end with Tasks.php. Create this directory, and inside it, create a file called scheduledSendTasks.phpand paste the following code into it:

<?php

use Crunz\Schedule;
use Courier\CourierClient;
use Dotenv\Dotenv;

// Configure environment variables - set the .env directory to the parent of the Tasks directory
// Environment variables are stored in a variable so that they can be passed to the scheduled task, which will not have access to the current global namespace
$dotenv = Dotenv::createArrayBacked(__DIR__ . "/..")->load();

// Configure scheduler
$schedule = new Schedule();

// Configure the Courier PHP SDK - note the first null is the API path, of which we will use the default
$courier = new CourierClient(null, $dotenv['COURIER_AUTHORIZATION_TOKEN']);

// Create a new scheduled task
$task = $schedule->run(function () use ($courier, $dotenv) {

    echo "Running " . __FILE__ . "\n";

    // Send notification using the Courier PHP SDK
    $notification = (object) [
        "to" => [
            "email" => $dotenv['TEST_EMAIL_TO']
        ],
        "template" => $dotenv['TEST_DIRECT_SEND_NOTIFICATION_TEMPLATE_ID'],
        "routing" => [
            "method" => "single",
            "channels" => ["email"]
        ],
        "data" => [
            "name" => "John Doe",
            "place" => "123 High Street",
        "salonName" => "Cutting Edge"
        ]
    ];
    $result = $courier->sendEnhancedNotification($notification);
});

// Schedule a single notification for a specific time
$task->on('13:30 2023-07-01')
    ->description('Sending scheduled email');

return $schedule;
Enter fullscreen mode Exit fullscreen mode

Now that you’ve created a scheduled task, it will run as soon as the scheduled time is reached. Remember, as mentioned above, the default timezone is UTC, but you can change this.

If you want to test your scheduled task, you can force it to run immediately using this command:

vendor/bin/crunz schedule:run --force
Enter fullscreen mode Exit fullscreen mode

The above command will run all scheduled or recurring tasks that you’ve created. If you want to be sure which tasks will run with this command, you can run another command to check how many scheduled tasks you have:

vendor/bin/crunz schedule:list
Enter fullscreen mode Exit fullscreen mode

This will output a table containing your scheduled tasks:

+---+------------------------------+-------------+-----------------+
| # | Task                         | Expression  | Command to Run  |
+---+------------------------------+-------------+-----------------+
| 1 | Sending scheduled email      | 30 13 1 7 * | object(Closure) |
| 2 | Sending recurring email      | 30 13 * * * | object(Closure) |
| 3 | Sending scheduled automation | 30 13 1 7 * | object(Closure) |
+---+------------------------------+-------------+-----------------+
Enter fullscreen mode Exit fullscreen mode

Sending a recurring email

You can also use Crunz to create recurring tasks on a schedule.

Inside your tasks directory, create a PHP file called recurringSendTasks.php and paste this code into it:

<?php

use Crunz\Schedule;
use Courier\CourierClient;
use Dotenv\Dotenv;

// Configure environment variables - set the .env directory to the parent of the Tasks directory
// Environment variables are stored in a variable so that they can be passed to the scheduled task, which will not have access to the current global namespace
$dotenv = Dotenv::createArrayBacked(__DIR__ . "/..")->load();

// Configure scheduler
$schedule = new Schedule();

// Configure the Courier PHP SDK - note the first null is the API path, of which we will use the default
$courier = new CourierClient(null, $dotenv['COURIER_AUTHORIZATION_TOKEN']);

// Create a new scheduled task
$task = $schedule->run(function () use ($courier, $dotenv) {

    echo "Running " . __FILE__ . "\n";

    // Send notification using the Courier PHP SDK
    $notification = (object) [
        "to" => [
            "email" => $dotenv['TEST_EMAIL_TO']
        ],
        "template" => $dotenv['TEST_DIRECT_SEND_NOTIFICATION_TEMPLATE_ID'],
        "routing" => [
            "method" => "single",
            "channels" => ["email"]
        ],
        "data" => [
            "name" => "John Doe",
            "place" => "123 High Street",
            "salonName" => "Cutting Edge"
        ]
    ];
    $result = $courier->sendEnhancedNotification($notification);
});

// Set up a recurring task
$task
    ->daily()
    ->at('13:30')
    ->description('Sending recurring email');

return $schedule;
Enter fullscreen mode Exit fullscreen mode

To test this, again run vendor/bin/crunz schedule:run --force. However, you’ll only receive one email, as you are using the –-force option, which forces a single run of each task. When your recurring task is being invoked by Crunz as a scheduled task, it will be called at the specified interval, and if you use the above code example, you will receive one email per day at 13:30.

Using Laravel? It’s already got scheduling baked in

If you’re using Laravel, you don’t need to worry about setting up your own scheduling solution, as it already has its own(https://laravel.com/docs/10.x/scheduling) (and queueing!) built in — one of the many advantages of using a PHP framework.

Option 2: use Courier’s automations to add send logic to your scheduled emails

Sometimes you need to add some logic around the sending of your scheduled or recurring emails, and this is where Courier’s automations can be useful. Automations allow you to chain together a series of different steps, including the sending of emails based on different notification templates.

Examples of when to use automations

Imagine a gym that sends its customers workout tips — it may want to sometimes send out different workout plans based on customers’ age or other groupings. Using automation in Courier, it could implement some basic branching logic such as “if the customer’s age is greater than 50, send an email using the over 50s email template; otherwise, send using the under 50s email template.”

Another feature that Courier automations offer is the ability to add a delay between two different actions. To reuse our hairdresser example, imagine that in addition to reminding the customer of their appointment the day before, they also want to send an email the day after their appointment to thank them and offer them 10% off their next appointment. Here’s how to implement this using Courier’s automations:

Sending two scheduled emails in one automation

As this involves sending two different emails, you will need to create a second email template for the 10% off offer. Create a new template with the following body:

Dear {name}, 

We hope you were satisfied with your hair appointment yesterday, and
we would like to offer you 10% off your next booking.

Thanks,

The {salonName} Team
Enter fullscreen mode Exit fullscreen mode

Now that you have both email templates ready to go, you can create your automation in Courier. The automation sends the first email, then there is a delay (in the example below, there is a two-minute delay, but for real-world use, you would probably set it to two days), and then it sends a follow-up email. We will continue to use the PHP Crunz package to kick off the automation at the right moment (one day before the customer’s scheduled appointment), meaning that the second follow-up email will be sent one day after their appointment.

To follow along with this example, you will need to have followed the steps in the “Prepare your PHP environment” step explained earlier.

In your tasks directory in your PHP project, create a file called scheduleAutomationTasks.php and paste in the following code:

<?php

use Crunz\Schedule;
use Courier\CourierClient;
use Dotenv\Dotenv;

// Configure environment variables - set the .env directory to the parent of the Tasks directory
// Environment variables are stored in a variable so that they can be passed to the scheduled task, which will not have access to the current global namespace
$dotenv = Dotenv::createArrayBacked(__DIR__ . "/..")->load();

// Configure scheduler
$schedule = new Schedule();

// Configure the Courier PHP SDK - note the first null is the API path, of which we will use the default
$courier = new CourierClient(null, $dotenv['COURIER_AUTHORIZATION_TOKEN']);

// Create a new scheduled task
$task = $schedule->run(function () use ($courier, $dotenv) {

    echo "Running " . __FILE__ . "\n";

    // Invoke an automation using the Courier PHP SDK
    $automation = (object) [
        "steps" => [
            [
                "action" => "send",
                "recipient" => $dotenv['TEST_AUTOMATION_RECIPIENT_USER_ID'],
                "template" => $dotenv['TEST_AUTOMATION_NOTIFICATION_TEMPLATE_ID_1'], // Reminder email
                "brand" => $dotenv['YOUR_COURIER_BRAND_ID'],
                "data" => [
                      "name" => "John Doe",
                      "salonName" => "Cutting Edge"
                  ]
            ],
            [
                "action" => "delay",
                "duration" => "2 minutes" // You will probably want to delay by days or hours, but minutes are easier for testing
            ],
            [
                "action" => "send",
                "recipient" => $dotenv['TEST_AUTOMATION_RECIPIENT_USER_ID'],
                "template" => $dotenv['TEST_AUTOMATION_NOTIFICATION_TEMPLATE_ID_2'], // Follow-up email
                "brand" => $dotenv['YOUR_COURIER_BRAND_ID'],
                "data" => [
                      "name" => "John Doe",
                      "salonName" => "Cutting Edge"
                  ]
            ]
        ]
    ];
    $result = $courier->invokeAutomation($automation);
});

// Schedule the automation for a specific time
$task->on('13:30 2023-07-01')
    ->description('Sending scheduled automation');

return $schedule;
Enter fullscreen mode Exit fullscreen mode

Ensure you’ve updated your .env file with any configuration you need to run this automation:

COURIER_AUTHORIZATION_TOKEN Courier API key, found in Settings
TEST_AUTOMATION_RECIPIENT_USER_ID Find your user ID in Courier’s list of users
TEST_AUTOMATION_NOTIFICATION_TEMPLATE_ID_1 The notification template ID for the “Hair appointment reminder” email template
TEST_AUTOMATION_NOTIFICATION_TEMPLATE_ID_2 The notification template ID for the “Hair appointment — 10% off” email template
YOUR_COURIER_BRAND_ID Choose your brand and find its ID in its “brand settings” or URL

Now run vendor/bin/crunz schedule:run --force, and you will receive the two different emails with the specified delay in between.

Dynamic automations API documentation

To understand all the features that automations can offer, you can play around with our dynamic request builder in Courier’s [automation API documentation][20]. This allows you to build up your PHP automation request dynamically by adding request parameters. For example, if you add your preferred Courier brand ID in the “brand” box, your brand ID will be automatically added to a PHP request on the box on the right. You will need to select the “PHP” button to get a PHP request; however, other languages are available at the click of a button.

One of the key features of a Courier automation is the series of “steps” that make it up. To understand the different steps that can be part of an automation, see Courier’s extensive documentation.

How to use the dynamic request builder tool in Courier’s automation API documentation — a convenient visual tool to help you explore the structure of an automation API request object.

Option 3: use Courier’s no-code automations designer to build complex logic around scheduled emails

The automations designer is a UI tool for building automation templates in Courier. An automation template offers a way to reuse Courier automations, and because they can be created in the Courier UI, they are super easy to create. Even your non-developer colleagues will be able to create automation templates using Courier’s simple drag-and-drop canvas.

For this example, we will use a “remember to pay your taxes” email, as this email could be sent on a schedule (April 1 of this year) or as a recurrence (April 1 every year).

Create a notification template

Create a new notification template with the subject “Tax deadline approaching,” and use the AI content generator to create some text for the body of your email. This may looks something like this:

Dear {name},

The end-of-year tax deadline is fast approaching. If you haven't yet filed your taxes, you can do this using our app:

{appUrl}

If you have any questions or concerns, please don't hesitate to contact us.

Thank you,

The {companyName} Team
Enter fullscreen mode Exit fullscreen mode

Now that your template is complete, click Publish Changes.

Screenshot showing how to create an email template in Courier.

Create an automation template

Navigate to automations, and click New Automation. Rename your automation from “Untitled Automation” to “Tax Deadline Reminder.”

To define the trigger for your automation, drag the Schedule trigger onto the canvas.

For a one-off scheduled email, change the Type of the Schedule node to Date, and enter the date and time you want your notification to be sent — in this case, we will choose midnight on April 1, 2024.

Screenshot showing how to schedule an email in the Courier automations designer.

For a recurring email, change the Type to Recurrence. Then set a start date of April 1, 2024, 00:00, an end date of April 1, 2028, 00:00, and a frequency of Yearly. This will ensure the reminder email is sent for the next five years.

Next, drag a Send action onto the canvas, and ensure a line connects the bottom of the Schedule node to the top of the Send node so that it’s clear that the send action follows the schedule trigger.

Enter refs.data.user_id as the user that the email should be sent to, and select your “Tax deadline approaching” notification template from the drop-down box. Now, click on Edit next to Advanced to edit some advanced properties.

Screenshot showing how to edit advanced properties for your email automation.

We will use the Advanced area to add some data to send to your automation template. This includes the user_id referred to in the previous paragraph plus any variables that your Tax deadline approaching notification template may be expecting.

Add this JSON to the Data section of the Advanced area:

{
  "name": "John Doe",
  "user_id": "courier-user-id",
  "appUrl": "example.com/file-taxes",
  "companyName": "Acme Ltd"
}
Enter fullscreen mode Exit fullscreen mode

Screenshot showing how to add the user ID and other variables to your email automation.

Finally, click Publish changes. Your email notification will be sent on April 1, 2024, at midnight.

Sending digests on a recurring schedule

If your reason for sending recurring emails is that your users are subscribed to regular news or updates from your app, you may want to set up an email digest. A digest allows you to avoid overwhelming your users with too many emails by condensing a large number of emails into one regular email update. Courier now offers the ability to send email digests. For more information, see the documentation.

Conclusion

Sending scheduled or recurring emails is not the simplest thing to do in PHP, as it doesn't have a task scheduler built into the language. On top of this, using a third party library like Crunz to deal with scheduling includes an extra layer of complication, in that you have to edit your crontabfile to hand over scheduling responsibility from your cron system to Crunz. This requires at least a basic understanding of how cron jobs work, including the syntax needed to run them.

The simpler option is to remove any need for scheduling tasks in PHP by using Courier's automation designer. This is a no-code solution that brings all your send logic inside the Courier UI. Aside from simplicity, an advantage of this is it allows your non-developer colleagues to be able to review or edit the logic around sending scheduled emails.

On the other hand, if it's more important to you to keep your send logic locked down and version controlled, and if you're prepared to get to grips with using Crunz, then you just need to decide whether you want to send a simple email (either a single email or a recurring one) — in which case you should use option 1 — or if you need to add more logic to the sending of your notifications, with the possibility of chaining together multiple steps — in which case option 2 is best.

If you haven’t yet joined Courier, you can sign up today and get started immediately. If you’re an existing Courier user, you can easily try scheduling one of your existing notification templates in our automations designer, or use our PHP SDK to get started with scheduled and recurring emails.

Top comments (0)