Skip to main content

Segmentation

This document covers the segmentation capabilities of the CRM & Marketing Platform.


Key Design: CRM Owns Segmentation

CRM defines segments using rich cross-service data. Messaging executes delivery.

CRM                                    Messaging
┌─────────────────────┐ ┌─────────────────────┐
│ CustomerSegment │ │ Segment (simplified)│
│ ─────────────────── │ sync │ ─────────────────── │
│ Rules using: │ ──────────────▶│ Contact membership │
│ - SCL tier │ │ only (for delivery) │
│ - TeeTime bookings │ │ │
│ - CRM engagement │ │ │
│ - Messaging consent │ │ │
└─────────────────────┘ └─────────────────────┘

Segment Types

TypeDescriptionExample
StaticManual membership"VIP List", "Board Members"
DynamicRule-based, auto-computed"Active members with handicap < 10"
SmartML-powered"Likely to churn in 30 days"

Dynamic Segment Rules

Segments can use data from ALL services:

const activeGolferSegment = {
name: "Active Golfers - Low Handicap",
rules: [
// From SCL
{ field: "membershipStatus", operator: "eq", value: "ACTIVE" },
{ field: "membershipTier", operator: "in", value: ["Gold", "Platinum"] },

// From TeeTime (denormalized)
{ field: "handicap", operator: "lt", value: 15 },
{ field: "bookingCount30d", operator: "gte", value: 2 },

// From CRM
{ field: "engagementScore", operator: "gte", value: 50 },

// From Messaging (denormalized consent)
{ field: "emailOptIn", operator: "eq", value: true }
],
combinator: "AND"
};

Rule Operators

OperatorDescriptionExample
eqEqualsstatus eq "ACTIVE"
neqNot equalsstatus neq "INACTIVE"
gtGreater thanengagementScore gt 50
gteGreater than or equalbookingCount30d gte 2
ltLess thanhandicap lt 15
lteLess than or equalchurnRiskScore lte 30
inIn arraymembershipTier in ["Gold", "Platinum"]
containsString containsemail contains "@company.com"
isNullIs nullhandicap isNull
isNotNullIs not nulllastActivityAt isNotNull

Available Fields

From CRM (Direct)

FieldTypeDescription
statusenumACTIVE, INACTIVE, MERGED
lifecycleStageenumSUBSCRIBER → ADVOCATE
engagementScorenumber0-100
churnRiskScorenumber0-100
lifetimeValuenumberTotal spend (cents)
lastActivityAtdateLast interaction
activityCount30dnumberActivities in 30 days
tagsarrayCustomer tags

From SCL (Synced)

FieldTypeDescription
membershipStatusstringACTIVE, INACTIVE, etc.
membershipTierstringGold, Silver, etc.
memberSincedateMembership start
membershipExpirydateMembership end

From TeeTime (Synced)

FieldTypeDescription
handicapnumberCurrent handicap
homeClubIdstringHome club
bookingCount30dnumberBookings in 30 days
lastBookingAtdateLast tee time

From Messaging (Synced)

FieldTypeDescription
emailOptInbooleanEmail consent
smsOptInbooleanSMS consent
pushOptInbooleanPush consent
whatsappOptInbooleanWhatsApp consent
lastEmailOpenAtdateLast email open
lastEmailClickAtdateLast email click

Segment Refresh

Refresh Frequencies

FrequencyUse Case
ManualStatic segments, one-off
HourlyTime-sensitive campaigns
DailyRegular newsletters
WeeklyMonthly reports

Refresh Process

1. Parse segment rules
2. Build Prisma WHERE clause
3. Query CustomerProfile table
4. Update CustomerSegmentMembership
5. Update memberCount on segment
6. Emit segment.membership.changed events

Messaging Sync

When a segment is used for a campaign, CRM syncs membership to Messaging:

// CRM → Messaging sync
async syncToMessaging(segmentId: string): Promise<void> {
const segment = await this.getSegmentWithMembers(segmentId);

// Create/update segment in Messaging
const messagingSegment = await this.messagingClient.upsertSegment({
externalId: segment.id,
name: segment.name,
contacts: segment.memberships.map(m => ({
email: m.customer.email,
phoneNumber: m.customer.phoneNumber,
firstName: m.customer.firstName,
lastName: m.customer.lastName,
})),
});

// Store reference
await this.updateSegment(segmentId, {
messagingSegmentId: messagingSegment.id,
lastSyncedAt: new Date(),
});
}

System Segments

Pre-defined segments that are auto-managed:

SegmentDescriptionRules
All CustomersEveryonestatus = ACTIVE
Active MembersCurrent membersmembershipStatus = ACTIVE
High EngagementEngaged customersengagementScore >= 70
At RiskLikely to churnchurnRiskScore >= 70
Email SubscribersEmail opted-inemailOptIn = true
SMS SubscribersSMS opted-insmsOptIn = true

AI Segment Builder

For creating segments using natural language, see the AI Segment Builder which converts queries like:

  • "Find platinum members who haven't booked in 60 days"
  • "High engagement customers at risk of churning"
  • "Email subscribers in South Africa"

Into validated segment criteria JSON.