How To Schedule Follow Up Emails In WordPress

On a recent project, I needed to have a series of emails following up new users, prompting them to setup their profile. I couldn’t use Aweber, like I normally would, because the follow up emails had to stop when they completed their profile. That could happen at any time

So how did I schedule follow up emails in WordPress?

  1. Store new users in a follow up array in the database
  2. Create a follow up email sequence based on days since registered
  3. Run the follow up on a wp_cron sequence
  4. Remove users from the follow up who have done what you asked

Store New Users in a Follow Up Array in the Database

You need to create a list of people for following up. I already had a process that hooked into user registration, which you can read about here. So I built upon that code. If you want to do the same, see the code on that post, then add the code from new_user_followup_registration set out below at the end of

if (get_user_meta($user->ID, 'membership_user_role', true) == 'job_seeker') {}

For this tutorial, I’ll assume you are using standalone code.

Put the following code into your functions.php:

add_filter('wp_new_user_notification_email', 'new_user_followup_registration', 10, 3);

function new_user_followup_registration($wp_new_user_notification_email, $user, $blogname)
{
 $new_users = get_option('new_user_followup');
 if (empty($new_users)) {
  $new_users = array();
 }
 
 $new_users[$user->user_login] = array(
  'ID' => $user->ID,
  'email' => $user->user_email,
  'registered' => time(),
  'last_email' => 'registration'
 );
 update_option('new_user_followup', $new_users);
 
 return $wp_new_user_notification_email;
}

The way this works is it runs just before the new user is sent the welcome email.

You can run a conditional if you just want to send the follow up to certain users (maybe only subscribers or a particular level of member).

You could also change the filter to something else, if you have a different trigger for initializing the sequence.

The list of users to follow up is stored in the wp_options table under “new_user_followup“.

There’s a check in case there are no current users to follow up.

Then we build a new array entry, pulling in the ID, email, time of registration and stage of follow up. You can certainly pull ’email’ and ‘registered’ from the user table when you do your later checks, but I chose to have it all together for speed and independence.

Finally we store the updated array with the new user in the wp_options table, then let the email continue processing. Make sure you leave that return in there or the user won’t get their welcome email.

Create a Basic Plugin for Follow Up

While you could build this functionality in your functions.php file or a separate file in your template, I chose to build this as a plugin.

The main reason for this was so I didn’t have to mess about with setting or unsetting the cron events.

It’s probably not entirely best practice to combine an entry in functions.php with a plugin. I decided to do it on this project because the code in functions.php is not dependent on the plugin. As we were already doing a custom function for the initial emails, it didn’t make sense to split the code into two places.

The entire plugin code is as follows:

<?php
/**
 * Plugin Name: New User Email Followup
 * Plugin URI: https://www.intelliwolf.com.au
 * Description: Follow Up Sequence For New User Registration
 * Author: Mike Haydon
 * Author URI: https://www.intelliwolf.com.au
 * Version: 1.0
 */

if(!defined('ABSPATH'))
 exit;

/* Set cron for followup profile completion emails */
register_activation_hook(__FILE__, 'new_user_followup_activation');

function new_user_followup_activation()
{
 if (!wp_next_scheduled('new_user_followup')) {
  wp_schedule_event(time(), 'daily', 'new_user_followup');
 }
}

register_deactivation_hook(__FILE__, 'new_user_followup_deactivation');

function new_user_followup_deactivation()
{
 wp_clear_scheduled_hook('new_user_followup');
}

add_action('new_user_followup', 'user_followup');

function user_followup() {
 $new_users = get_option('new_user_followup');
 if (empty($new_users)) {
  // nothing to process
  die();
 }

 $day = 60 * 60 * 24;
 $current_time = time();

 foreach ($new_users as $user => &$info)
 {
  if (empty($info['ID']) or empty($info['email'])) {
   unset($new_users[$user]);
   continue;
  }

  $new_user_profile_id = get_user_meta($info['ID'], 'member_profile', true);
  if (!empty($new_user_profile_id)) {
   unset($new_users[$user]);
   continue;
  }

  $days_since_registration = ($current_time - $info['registered']) / $day;
  if ($days_since_registration < 2) {
   continue;
  }

  if ($days_since_registration > 12) {
   wp_delete_user($info['ID']);
   unset($new_users[$user]);
   continue;
  }
  elseif ($days_since_registration > 7) {
   $email_to_send = 'final reminder';
  }
  elseif ($days_since_registration > 4) {
   $email_to_send = 'second reminder';
  }
  elseif ($days_since_registration >= 2) {
   $email_to_send = 'first reminder';
  }

  if (!empty($info['last_email']) and ($info['last_email'] == $email_to_send)) {
   continue;
  }

  $info['last_email'] = $email_to_send;
  $new_user_email = new_user_email($email_to_send);
  if (empty($new_user_email['subject']) or empty($new_user_email['message'])) {
   continue;
  }

  $headers = array("Content-Type: text/html; charset=UTF-8", "From: Website <[email protected]>");

  wp_mail($info['email'], $new_user_email['subject'], $new_user_email['message'], $headers);
 }

 update_option('new_user_followup', $new_users);
}

function new_user_email($stage)
{
 $subject = '';
 $message = '';
 switch($stage) {
  case 'first reminder':
   $subject = "Please Complete Your Profile";
   $message = "Thank you for your interest etc";
   break;
  case 'second reminder':
   $subject = "Reminder: Please Complete Your Profile";
   $message = "Thank you for your interest etc";
   break;
  case 'final reminder':
   $subject = "Final Reminder: Please Complete Your Profile";
   $message = "Thank you for your interest etc";
   break;
 }
 return array(
  'subject' => $subject,
  'message' => $message
 );
}
?>

You can use this as-is. Just put it into a file called

/new-user-email-followup/new-user-email-followup.php

Then zip the folder and upload it as a regular plugin.

If this is your first custom WordPress plugin, congratulations! At its most basic, that’s all there really is to it.

Let’s break the code down, shall we?

Initializing the Plugin

If you’re not familiar with building plugins, the top comment section is used by WordPress to create the dialogue on /wp-admin/plugins.php

The next two lines just make sure the file isn’t accessed directly.

Create the Schedule

We use what’s called a “Cron” to do repetitive tasks. WordPress has further refined that with wp_cron. You can read more about it here.

Essentially, you say how often you want a function to be run, then the system sets a timer and runs it when that timer says.

In this implementation, I set it to daily. Other default options are fiveminutes, hourly, twicedaily, weekly and monthly. Note this only affects how often we’re checking, not how often the emails are sent. If you have a lot of registrants every day, it’s probably best to do the check more often and reduce the email load.

In practice, and this is important for low traffic, new sites, a wp_cron only checks its schedule if someone loads a page on the site (including the admin area). I’ve usually gotten around that by running an uptime checker.

The next section of code creates a daily run of the action “new_user_followup“, which runs the function user_followup().

The register_activation_hook and register_deactivation_hook is what creates the wp_cron when you activate the plugin and removes the wp_cron when you deactivate the plugin.

Create the Followup Sequence

The function user_followup() is the engine of this implementation.

First we pull the current list of new users who need following up from the database. We quit if there are none.

Next we set some timing variables. I could have set

$day = 86400;

But it makes more sense for someone reading the code to use the obvious reference of 60 x 60 x 24.

I set the time to a variable, just in case the process ran long or got stuck, which could cause some timing irregularities on the loop.

Looping Through the New Users

Notice in the foreach loop initialisation how I use

as $user => &$info

The &$info is important. It directly calls the data, rather than passing in a copy. This allows me to change the array as I loop through it. I’m using that feature to selectively change the value of [‘last_email’] as the loop runs.

The first if statement is an error check. We remove the user from the list if the ID or email is empty.

If you’re not familiar with php loops, the continue statement ends the current iteration of the loop and moves on to the next, without processing anything else in that round.

Should We Stop Sending Reminders?

In the next line, we check whether the new user has completed their profile.

$new_user_profile_id = get_user_meta($info['ID'], 'member_profile', true);
if (!empty($new_user_profile_id)) {
 unset($new_users[$user]);
 continue;
}

Elsewhere in our implementation, when a user completes their profile, we add an entry to wp_usermeta for that user_id that assigns the profile post id to member_profile.

It doesn’t matter to us what the value of member_profile for that user is, just that they have one.

You may have a different implementation. If you’re running a follow up sequence that depends on a user completing their profile, amend this section to fit your implementation.

If you’re just using this to create an autoresponder sequence for new users, then you don’t need this section.

Calculate How Long Since Registration

$days_since_registration = ($current_time - $info['registered']) / $day;
if ($days_since_registration < 2) {
 continue;
}

In this section, we calculate how long since the user registered. If they’ve only just registered in the last couple of days, we don’t bother them.

Remove From Sequence and Delete User

if ($days_since_registration > 12) {
 wp_delete_user($info['ID']);
 unset($new_users[$user]);
 continue;
}

In our implementation, if 12 days have gone by since the user registered and they still haven’t completed their profile, we delete them from the site as a user and remove them from the follow up sequence.

Otherwise, we set which reminder is the next to send, based on how long since they’ve registered.

If you don’t want to remove the user after they’ve gone through the email sequence, delete the line:

wp_delete_user($info['ID']);

Should We Even Send an Email?

if (!empty($info['last_email']) and ($info['last_email'] == $email_to_send)) {
 continue;
}
$info['last_email'] = $email_to_send;

If the next reminder we need to send to the user hasn’t changed, there’s no need to send them anything.

If it has changed, we make sure to update that user’s next email. If every email were super-critical, we’d probably not add the line

$info['last_email'] = $email_to_send;

until after we checked that wp_mail successfully ran.

Figure Out Which Email to Send and Send it

$new_user_email = new_user_email($email_to_send);
if (empty($new_user_email['subject']) or empty($new_user_email['message'])) {
 continue;
}

$headers = array("Content-Type: text/html; charset=UTF-8", "From: Website <[email protected]>");

wp_mail($info['email'], $new_user_email['subject'], $new_user_email['message'], $headers);

First we call the new_user_email() function and get back an array with the subject and message. Then we do an error check to make sure we’ve got something to send.

For email delivery, it’s important to add the headers. Change Website <[email protected]> to your name and email.

wp_mail($to, $subject, $message, $headers) is the function that actually sends the email. This is WordPress’s standard email function. It’s the first place I’d look if I were having issues with sending. You can read more about it at the WordPress Developer Reference Manual.

You can also add attachments to your emails with this function, but that’s outside the scope of this tutorial – check the Reference Manual.

Update the User Follow Up List

update_option('new_user_followup', $new_users);

After processing all the users in the list, we update the database ready for next time. This will throw out the users we removed using unset and update any users for whom we changed the last_email.

Set the Subject and Message for each Follow Up

I split the listing of the subject and message into the separate function new_user_email($stage) to improve readability. I could have put them in the if ($days_since_registration) blocks, but there is a lot of text. It only doesn’t look like it in the code example because I truncated it for this tutorial.

In the switch statement, you can have as many cases as you like. Just make sure to add a break after each. Everything else in that function should be pretty self explanatory.

It’s important to use double quotes in the $message so that you can use html. To add a line, use \r\n.

Try not to use any more html than you absolutely need. This will improve deliverability and decrease the chance that your emails go to the spam box.

I hope you found this guide useful. Please let me know in the comments below if you had any issues with the code, or if I need to explain something more clearly.

Leave a Comment