Journey 5: Re-engagement

No-show recovery, warm lead nurture, and long-term drip sequences

Trigger event
Sequence selection
Touchpoints
Preference learning
Conversion or long-term drip
STEP 1 System Detection

A trigger event fires

Re-engagement starts automatically — no one has to notice the gap or remember to follow up. Three distinct triggers kick off the process: a patient no-shows for a confirmed appointment, a warm lead goes quiet after initial contact, or a converted patient hasn't rebooked within the expected window after completing a care plan.

▶ Trigger sources and detection logic
Source files
src/nurture.js
src/contacts.jsmarkWarmLead() (line 270)
What happens
Three trigger paths:

(a) No-show: Cal.com fires a booking.noShow webhook when a patient misses their appointment. The webhook handler marks the contact and queues a re-engagement job immediately.

(b) Warm lead gone quiet: Activity tracking detects when a warm lead hasn't replied in a configured window (default 48 hours). markWarmLead() in contacts.js tags the contact, and an inactivity check fires enrollment.

(c) Lapsed patient: When a care plan completes and no new booking is detected within 30 days, the care plan completion handler checks for a follow-up booking gap and triggers the lapsed patient sequence.
DB tables
contacts, nurture_enrollments
STEP 2 Nurture System

Contact enrolled in nurture sequence

Once a trigger fires, Karen selects the right sequence based on the contact's situation. Each sequence is sized and paced for the relationship: a warm lead that ghosted needs gentle re-entry over two weeks; a no-show needs faster, more direct outreach; a lapsed patient gets a warmer "we miss you" tone that acknowledges the prior relationship.

▶ Sequence creation and scheduling
Source files
src/nurture.jsenrollContact(), createSequence(), activateSequence()
What happens
enrollContact() checks whether the contact is already in an active sequence (no double-enrollment). createSequence() builds the appropriate sequence definition based on trigger type:

Warm lead: 5 touches over 14 days (days 1, 3, 7, 10, 14)
No-show: 3 touches over 7 days (days 1, 4, 7)
Lapsed patient: "We miss you" — 2 touches spaced 5 days apart, warmer tone

activateSequence() schedules each step as a BullMQ delayed job. The jobs sit in the queue and fire at the right time without any manual intervention.
DB tables
nurture_sequences, nurture_steps, nurture_enrollments
STEP 3 Karen AI → Patient

Automated touchpoints go out

Over the coming days and weeks, Karen sends personalized messages — not generic blasts. She remembers what procedure the patient asked about, references their previous conversation, and varies the content type across the sequence. A 5-touch warm lead sequence looks like this: Day 1 (helpful information about the procedure they asked about), Day 3 (social proof — a patient story or result), Day 7 (a limited-time offer or availability nudge), Day 10 (educational content that deepens the "why"), Day 14 (a final, direct booking nudge).

▶ Personalized message generation
Source files
src/nurture.js BullMQ worker (processes queued steps)
src/ai.jschat() (generates personalized content per step)
What happens
When a BullMQ step job fires, the nurture worker calls chat() with the contact's full context — their conversation history, the procedure they asked about, their stated interests, and the step's content objective (info / social proof / offer / education / nudge). Karen generates a unique, personalized message for each contact rather than sending a template. The message is delivered via the contact's preferred channel (WhatsApp or Instagram) through Chatwoot.
DB tables
nurture_enrollments
STEP 4 Memory System

Preference learning adapts messaging

As the sequence runs, Karen pays attention. If a contact opens and replies quickly at 7pm, she notes that timing. If they engage with educational content but skip the offer messages, she adjusts emphasis. Price sensitivity signals — questions about cost, hesitation language — are stored and used to calibrate future messaging tone.

▶ Preference signals and storage
Source files
src/actions/engage/preferences.js
src/contacts.jscontact_preferences table
What happens
Every reply during the sequence is processed through the preference learning system. Engagement signals (reply speed, message length, content engagement) are stored in contact_preferences with a confidence score that increases with each matching signal. These preferences feed back into buildContext() on the next interaction, so future messages from Karen — not just nurture steps — reflect what this person actually responds to.
DB tables
contact_preferences, memory_knowledge
STEP 5 Outcome

Conversion or long-term drip

If the patient books at any point during the sequence, the remaining nurture steps are automatically cancelled — no awkward follow-ups after they've already committed. They flow into Journey 2 (booking confirmed) and then Journey 4 (post-care) as normal. If the full sequence completes without a booking, the contact enters a monthly long-term drip: one thoughtful message per month for at least three months, keeping Oshun top-of-mind without being pushy.

▶ Auto-cancellation and long-term drip
Source files
src/nurture.js (auto-cancellation on booking event)
What happens
A Cal.com booking.created webhook triggers a lookup of all active nurture enrollments for that contact. Any pending steps are cancelled by removing them from the BullMQ queue. If no booking occurs after the final step, the system creates a low-frequency drip enrollment — one message per month — and schedules the first job 30 days out.
DB tables
nurture_enrollments, nurture_steps
Note
Long-term monthly drip is approved and designed. Implementation is pending — the sequence definition exists but the auto-enrollment trigger on sequence completion has not yet been wired up.