Skip to main content

Marketing Automation

This document covers the marketing automation (journeys) capabilities of the CRM & Marketing Platform.


Journey Builder

Multi-step automated sequences triggered by events or schedules.

┌─────────────────────────────────────────────────────────────────┐
│ NEW MEMBER ONBOARDING JOURNEY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [TRIGGER: member.created] │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ SEND Welcome │ │
│ │ Email │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ WAIT 3 days │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ YES ┌─────────────────┐ │
│ │ CONDITION: │───────────▶│ SEND Tips Email │ │
│ │ Opened welcome? │ └─────────────────┘ │
│ └────────┬────────┘ │
│ │ NO │
│ ▼ │
│ ┌─────────────────┐ │
│ │ SEND SMS │ │
│ │ Reminder │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ WAIT 7 days │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ END │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Event Triggers

Trigger EventSourcePossible Actions
member.createdSCLWelcome sequence
teetime.bookedTeeTimeBooking confirmation
teetime.completedTeeTimeReview request
handicap.updatedTeeTimeCongratulations message
payment.failedSCLPayment reminder
engagement.droppedCRMWin-back campaign
competition.publishedTeeTimeTournament promotion
churn.risk.highCRMRetention sequence

Journey Step Types

TypeDescriptionConfiguration
TRIGGEREntry pointeventType, conditions
SENDSend messagechannel, templateId
WAITDelay next stepduration (e.g., "3 days")
CONDITIONBranch based on criteriarules, nextStepYes, nextStepNo
SPLITA/B testvariants[], weights
UPDATEUpdate profilefield, value
ENDTerminate journey-

Step Configuration Examples

SEND Step

{
"type": "SEND",
"name": "Welcome Email",
"config": {
"channel": "EMAIL",
"templateId": "welcome-email-v2"
},
"nextStepId": "step_wait_3d"
}

WAIT Step

{
"type": "WAIT",
"name": "Wait 3 Days",
"config": {
"duration": "3 days"
},
"nextStepId": "step_condition"
}

Supported duration formats:

  • "30 minutes"
  • "2 hours"
  • "3 days"
  • "1 week"

CONDITION Step

{
"type": "CONDITION",
"name": "Check Email Opened",
"config": {
"rules": [
{ "field": "lastEmailOpenAt", "operator": "isNotNull" }
],
"combinator": "AND"
},
"nextStepYesId": "step_tips",
"nextStepNoId": "step_sms"
}

SPLIT Step (A/B Test)

{
"type": "SPLIT",
"name": "A/B Test Subject Line",
"config": {
"variants": [
{ "name": "A", "weight": 50 },
{ "name": "B", "weight": 50 }
]
},
"nextStepAId": "step_variant_a",
"nextStepBId": "step_variant_b"
}

UPDATE Step

{
"type": "UPDATE",
"name": "Mark as Onboarded",
"config": {
"updates": [
{ "field": "tags", "operation": "add", "value": "onboarded" },
{ "field": "lifecycleStage", "value": "CUSTOMER" }
]
},
"nextStepId": "step_end"
}

Journey Lifecycle

DRAFT ──▶ ACTIVE ──▶ PAUSED ──▶ ACTIVE
│ │ │
│ │ │
▼ ▼ ▼
Edit Enrolling Resume/Stop
Customers

Status Descriptions

StatusDescription
DRAFTBeing built, not enrolling
ACTIVEEnrolling customers, processing steps
PAUSEDStopped enrolling, existing continue
ARCHIVEDDisabled, no activity

Enrollment

Automatic (Trigger-based)

When a matching event occurs:

// Event: member.created with tier = "Gold"
// Journey: trigger = "member.created", conditions = [tier eq "Gold"]

// 1. Event received
// 2. Match against active journeys
// 3. Check trigger conditions
// 4. If match, enroll customer
// 5. Queue first step

Manual

await enrollCustomer(journeyId, customerId);

Re-entry

SettingBehavior
allowReentry: falseCustomer can only be enrolled once
allowReentry: trueCustomer can re-enter after completion

Journey Engine

Processing Flow

1. Enrollment created
2. First step queued
3. Step processor picks up job
4. Execute step (send, wait, etc.)
5. Determine next step
6. Queue next step or complete

Wait Step Processing

WAIT steps are handled by a cron job:

// Every minute, check for expired waits
const expiredEnrollments = await findEnrollments({
status: 'ACTIVE',
waitingUntil: { lte: now() }
});

for (const enrollment of expiredEnrollments) {
await progressToNextStep(enrollment);
}

Journey Stats

MetricDescription
totalEnrolledTotal ever enrolled
totalCompletedCompleted journey
totalExitedExited early
activeCountCurrently in journey

Per-Step Stats

MetricDescription
enteredReached this step
completedFinished this step
avgDurationTime in step

Exit Conditions

Customers can exit a journey:

  1. Completed - Reached END step
  2. Manual exit - Admin removes them
  3. Unsubscribed - Opted out during journey
  4. Merged - Profile merged into another
  5. Failed - Step execution failed