Automated follow-up for leads that don’t respond immediately — with status checks at execution time, not enrollment time.
A nurture sequence is an automated series of emails that sends based on time and behavior — not on someone manually deciding “this lead needs a follow-up.” Without it, leads that don’t respond to the first contact fall out of the process entirely. With it, every lead that doesn’t close immediately enters a systematic follow-up path without the sales team doing anything additional.
The Anchor build was converting leads at the point of first contact — if the lead picked up the phone, they bought. If they didn’t, nothing happened. The sales team’s time was consumed by active conversations; there was no bandwidth to circle back to leads that hadn’t responded.
The lost-revenue math: if 10% of non-responders close with consistent follow-up over 30 days, and the average deal is $15,000, and the business receives 40 non-responsive leads per month — that’s 4 potential deals per month that were never followed up on. At $15K each, that’s $60K/month in recoverable revenue being left on the table.
When a lead is created, it’s enrolled in the appropriate sequence based on source_form. The enrollment creates a scheduled WordPress cron event for each step:
function [client]_enroll_in_sequence( int $lead_id, string $sequence_name ) {
$sequences = [client]_get_sequence_config();
if ( ! isset( $sequences[ $sequence_name ] ) ) return;
$steps = $sequences[ $sequence_name ]['steps'];
foreach ( $steps as $index => $step ) {
wp_schedule_single_event(
time() + $step['delay_seconds'],
'[client]_sequence_step',
[ $lead_id, $sequence_name, $index ]
);
}
}
Sequences are defined as PHP arrays. Each step has a delay in seconds from enrollment, a template name, and optional conditions that must be true for the step to fire:
function [client]_get_sequence_config(): array {
return [
'unit_inquiry_nurture' => [
'steps' => [
[
'delay_seconds' => 3600, // 1 hour
'template' => 'nurture-day0-unit',
'conditions' => [ 'status_not' => [ 'closed_won', 'closed_lost' ] ],
],
[
'delay_seconds' => 86400, // 24 hours
'template' => 'nurture-day1-followup',
'conditions' => [ 'status_not' => [ 'contacted', 'qualified', 'closed_won', 'closed_lost' ] ],
],
[
'delay_seconds' => 259200, // 3 days
'template' => 'nurture-day3-value',
'conditions' => [ 'status_not' => [ 'qualified', 'closed_won', 'closed_lost' ] ],
],
[
'delay_seconds' => 604800, // 7 days
'template' => 'nurture-day7-last-chance',
'conditions' => [ 'status_not' => [ 'qualified', 'closed_won', 'closed_lost' ] ],
],
],
],
'financing_nurture' => [
'steps' => [
[
'delay_seconds' => 1800, // 30 min — hot
'template' => 'financing-followup-fast',
'conditions' => [ 'status_not' => [ 'contacted', 'qualified', 'closed_won', 'closed_lost' ] ],
],
// ...
],
],
];
}
When a scheduled cron event fires, the step handler checks the lead’s current status against the step’s conditions before sending. A lead that became qualified or closed_won between enrollment and the step firing does not receive the follow-up:
add_action( '[client]_sequence_step', '[client]_execute_sequence_step', 10, 3 );
function [client]_execute_sequence_step( int $lead_id, string $sequence, int $step_index ) {
$lead = [client]_get_lead_by_id( $lead_id );
if ( ! $lead ) return;
$sequences = [client]_get_sequence_config();
$step = $sequences[ $sequence ]['steps'][ $step_index ] ?? null;
if ( ! $step ) return;
// Check conditions — don't send to leads that have progressed
if ( isset( $step['conditions']['status_not'] ) ) {
if ( in_array( $lead->status, $step['conditions']['status_not'], true ) ) {
[client]_append_activity(
$lead_id,
'sequence_skip',
"Step {$step_index} skipped — lead status is {$lead->status}"
);
return;
}
}
// Check suppression
if ( [client]_email_is_suppressed( $lead->email ) ) return;
$context = [client]_build_sequence_email_context( $lead_id, $sequence, $step );
$body = [client]_render_email_template( $step['template'], $context );
$sent = wp_mail(
$lead->email,
$context['subject'],
$body,
[ 'Content-Type: text/html; charset=UTF-8',
'Reply-To: ' . SALES_EMAIL ]
);
if ( $sent ) {
[client]_add_conversation( $lead_id, 'email_out', $body, $context['subject'] );
[client]_append_activity( $lead_id, 'sequence_step', "Sequence email sent: {$step['template']}" );
}
}
When a lead’s status changes to closed_won or closed_lost, the remaining scheduled steps are cancelled:
function [client]_unenroll_from_sequences( int $lead_id ) {
$sequences = [client]_get_sequence_config();
foreach ( $sequences as $sequence_name => $config ) {
foreach ( $config['steps'] as $index => $step ) {
wp_clear_scheduled_hook(
'[client]_sequence_step',
[ $lead_id, $sequence_name, $index ]
);
}
}
}
// Hook into status updates
add_action( '[client]_lead_status_changed', function( $lead_id, $new_status ) {
if ( in_array( $new_status, [ 'closed_won', 'closed_lost' ], true ) ) {
[client]_unenroll_from_sequences( $lead_id );
}
}, 10, 2 );
The conditional branching is what separates a nurture system from a spam system. If a lead has already been contacted, qualified, or closed, they should not receive the Day 3 “just following up” email. The check at step execution time (not enrollment time) handles the case where a lead’s status changed between when the step was scheduled and when it fires.
WP cron’s reliability depends on site traffic. On a low-traffic site, cron events fire only when pages are visited. For time-sensitive sequences (the 30-minute financing follow-up), it’s worth setting up a real cron trigger via hosting cron or an uptime monitor that pings the site regularly.
Three active sequences run on every new lead: unit inquiry nurture (4 steps, 7-day window), financing nurture (3 steps, 3-day window), and a re-engagement sequence that fires 30 days after a lead enters nurture status. The Day 1 follow-up email has a 38% open rate — the best-performing step in the sequence.
The unenrollment hook was added after a lead who had already purchased called to ask why they were receiving “follow-up” emails. The fix: status-change unenrollment prevents any sequence step from firing on a closed lead, regardless of when the step was scheduled.
wp_schedule_single_event() per step, not a recurring event. Single events are precise; recurring events run until explicitly cleared. Sequences have a defined end — don’t use recurring cron for that.Reply-To header pointing to the sales team. Sequence emails look like they come from the business. When a lead replies, that reply should reach a human — not bounce back to a no-reply address.Every lesson stays free — no account, no paywall, no email gate, ever. But if you’d rather have this system standing on your business than wire all 48 lessons yourself, leave your email. We’ll send you a direct line to a build — and you’ll be first to hear when we add new tools to the curriculum.
None of this gates a single lesson. The curriculum was free before you got here and it stays that way.
You came here to understand the system, and now you do. If you’d rather have it standing on your business than spend the next three months wiring it yourself, GAP Concierge is the same architecture from these lessons — a white-label AI agent that knows your catalog and captures your leads — set up for you, from $97/mo.
See GAP Concierge →