Campaign Management
This document covers the campaign management capabilities of the CRM & Marketing Platform.
Campaign Types
| Type | Description | Use Case |
|---|---|---|
| ONE_TIME | Single send | Tournament announcement |
| RECURRING | Scheduled repeats | Weekly newsletter |
| TRIGGERED | Event-based | Booking confirmation |
| JOURNEY | Multi-step sequence | Onboarding series |
Campaign Workflow
┌─────────────────────────────────────────────────────────────────┐
│ CAMPAIGN LIFECYCLE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ DRAFT ──▶ SCHEDULED ──▶ ACTIVE ──▶ COMPLETED │
│ │ │ │ │
│ │ │ ▼ │
│ │ │ PAUSED ─────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Edit Edit/Cancel Resume/Cancel │
│ │
└─────────────────────────────────────────────────────────────────┘
Status Transitions
| From | To | Trigger |
|---|---|---|
| DRAFT | SCHEDULED | User schedules campaign |
| SCHEDULED | ACTIVE | Scheduled time reached |
| SCHEDULED | DRAFT | User cancels schedule |
| ACTIVE | COMPLETED | All messages sent |
| ACTIVE | PAUSED | User pauses or error |
| PAUSED | ACTIVE | User resumes |
| PAUSED | CANCELLED | User cancels |
Campaign Creation Flow
// 1. Create campaign
const campaign = await createCampaign({
name: "Summer Tournament 2024",
type: "ONE_TIME",
segmentId: "active-golfers-segment-id",
channels: ["EMAIL", "SMS", "FACEBOOK"],
scheduledAt: "2024-06-01T09:00:00Z",
timezone: "Africa/Johannesburg"
});
// 2. Configure channel content
await setCampaignContent(campaign.id, {
email: { templateId: "tournament-invite-email" },
sms: { templateId: "tournament-invite-sms" },
social: {
content: "Join us for the Summer Tournament 2024!",
mediaUrls: ["https://..."],
platforms: ["FACEBOOK", "INSTAGRAM"]
}
});
// 3. Schedule sends
await scheduleCampaign(campaign.id);
// → Creates jobs in BullMQ
// → Email/SMS → Messaging Service
// → Social → Social Publisher
Channels
| Channel | Delivery | Content Source |
|---|---|---|
| Messaging Service | Template ID | |
| SMS | Messaging Service | Template ID |
| PUSH | Messaging Service | Template ID |
| Messaging Service | Template ID | |
| SOCIAL | Social Publisher | Inline content |
Targeting
Segment-Based
// Target a pre-defined segment
const campaign = {
segmentId: "high-value-members",
// ...
};
Filter-Based
// Define inline filters (creates ad-hoc segment)
const campaign = {
filters: {
combinator: "AND",
rules: [
{ field: "membershipStatus", operator: "eq", value: "ACTIVE" },
{ field: "emailOptIn", operator: "eq", value: true }
]
},
// ...
};
Scheduling
Immediate Send
await executeCampaign(campaign.id);
Scheduled Send
await scheduleCampaign(campaign.id, {
scheduledAt: "2024-06-01T09:00:00Z",
timezone: "Africa/Johannesburg"
});
Recurring Schedule
const campaign = {
type: "RECURRING",
recurPattern: "0 9 * * 1", // Every Monday at 9am
recurEndAt: "2024-12-31T23:59:59Z",
timezone: "Africa/Johannesburg"
};
Execution
When a campaign is executed:
- Refresh segment - Get latest membership
- Sync to Messaging - Update contact list
- Filter by consent - Respect opt-outs per channel
- Send via channel - Messaging for email/SMS, Social for posts
- Track interactions - Record sent, delivered, etc.
- Update stats - Denormalized counts
Campaign Stats
| Metric | Description |
|---|---|
targetCount | Total in segment |
sentCount | Successfully sent |
deliveredCount | Confirmed delivered |
openedCount | Email opens (unique) |
clickedCount | Link clicks (unique) |
convertedCount | Conversions tracked |
unsubscribedCount | Opted out after |
bouncedCount | Hard/soft bounces |
Computed Rates
| Rate | Formula |
|---|---|
deliveryRate | delivered / sent |
openRate | opened / delivered |
clickRate | clicked / opened |
conversionRate | converted / clicked |
A/B Testing (Phase 4)
const campaign = {
type: "ONE_TIME",
abTest: {
enabled: true,
variants: [
{ name: "A", weight: 50, templateId: "template-a" },
{ name: "B", weight: 50, templateId: "template-b" }
],
winnerCriteria: "OPEN_RATE",
testDuration: "24h",
testPercentage: 20 // Send to 20%, then winner to rest
}
};