EP-010: CRM, Loyalty & Campaigns
| Field | Value |
|---|---|
| Epic ID | EP-010 |
| Title | CRM, Loyalty & Campaigns |
| Owner | Product -- CRM & Guest Engagement |
| Status | Partial |
| Last Updated | 2026-03-05 |
| Target Release | R4 (Q3 2026) |
1. Business Objective
Build a comprehensive guest relationship management system that enables Indian hotel operators to run multi-tier loyalty programs, execute targeted multi-channel marketing campaigns, and gather structured feedback -- all within a single tenant-isolated platform. The CRM must drive measurable increases in repeat bookings, direct-channel revenue, and guest satisfaction (NPS).
2. Audience
| Role | Interaction |
|---|---|
| Marketing Manager | Creates campaigns, segments guests, monitors analytics |
| Loyalty Program Manager | Defines tiers, earn/burn rules, reward catalog |
| Front Desk Agent | Enrolls guests, redeems points at check-in/check-out |
| Revenue Manager | Reviews campaign ROI, adjusts promotional strategies |
| Guest (end-user) | Earns/redeems points, receives communications, fills surveys |
| Super Admin | Configures tenant-level CRM settings and channel integrations |
3. Scope
3.1 In Scope
- Multi-tier loyalty program (Silver, Gold, Platinum, Diamond) with configurable thresholds
- Points earn rules (stay-based, spend-based, activity-based) and burn/redemption rules
- Points expiry, transfer between members, and balance adjustments
- Reward catalog (room upgrades, F&B vouchers, spa credits, late check-out)
- Campaign management for 6 campaign types: promotional, transactional, feedback, newsletter, birthday, anniversary
- Multi-channel delivery: email (SendGrid), SMS (Twilio), WhatsApp Business API, push (Expo)
- Communication template engine with variable substitution (
{{guest_name}},{{booking_id}}, etc.) - Drip campaign visual builder with node-based workflow editor
- Guest segmentation engine using RFM (Recency, Frequency, Monetary) analysis
- Feedback surveys with configurable questions and NPS calculation
- A/B testing for campaign subject lines, content, and send times
- Campaign analytics: sent, delivered, opened, clicked, converted, unsubscribed
- Kafka event integration for real-time triggers (booking.confirmed, guest.checkedIn, etc.)
- Multi-tenant isolation via
tenantIdon all CRM entities
3.2 Out of Scope
- Third-party loyalty coalition programs (e.g., InterMiles, credit card reward partnerships)
- Social media campaign management (Meta Ads, Google Ads)
- Full marketing automation comparable to HubSpot/Marketo
- OTA review response automation (covered in EP-013 AI)
- Guest data import from legacy PMS systems (covered in EP-015 Migration)
4. Current Implementation Status
| Component | Service / Module | Status |
|---|---|---|
| Loyalty tier CRUD | crm-service / loyalty module | Built |
| Points earn/burn rules | crm-service / loyalty module | Built |
| Member enrollment | crm-service / loyalty module | Built |
| Reward catalog | crm-service / loyalty module | Built |
| Points expiry cron | crm-service / loyalty module | Not Started |
| Points transfer | crm-service / loyalty module | Partial |
| Campaign CRUD (6 types) | crm-service / campaigns module | Built |
| Template engine | crm-service / templates module | Built |
| Drip campaign builder UI | Dashboard CRM module | Built |
| Drip execution engine | crm-service / drip module | Not Started |
| Guest segmentation (manual) | crm-service / segments module | Built |
| RFM segmentation | crm-service / segments module | Not Started |
| Feedback surveys | crm-service / feedback module | Built |
| NPS calculation | crm-service / feedback module | Not Started |
| A/B testing UI | Dashboard CRM module | Built |
| A/B split logic (backend) | crm-service / campaigns module | Not Started |
| Campaign analytics | crm-service / analytics module | Partial |
| WhatsApp template approval | crm-service / templates module | Not Started |
| Dashboard CRM pages | 02-hmp-web-dashboard | Built |
5. Gap Analysis
| # | Gap | Impact | Effort | Priority |
|---|---|---|---|---|
| 1 | Drip campaign execution engine not connected to notification service | Drip workflows render in UI but never fire actual messages | High | P0 |
| 2 | A/B testing has UI but no backend split logic | Cannot run real A/B experiments; all recipients get variant A | Medium | P1 |
| 3 | NPS calculation not automated | Staff must manually compute NPS from raw survey responses | Medium | P1 |
| 4 | RFM-based segmentation not implemented | Segments are manual tag-based; no data-driven guest clustering | High | P1 |
| 5 | Points expiry cron job not implemented | Points never expire, creating unbounded liability | High | P0 |
| 6 | WhatsApp template approval workflow missing | Cannot send WhatsApp campaigns (Meta requires pre-approved templates) | Medium | P1 |
| 7 | Campaign delivery tracking incomplete | Open/click tracking works for email; SMS/WhatsApp delivery status not captured | Medium | P2 |
6. User Stories Summary
| ID | Title | Priority | Points | Sprint | Status |
|---|---|---|---|---|---|
| US-010-01 | Define Loyalty Tiers | P0 | 5 | 1 | Built |
| US-010-02 | Member Enrollment | P0 | 5 | 1 | Built |
| US-010-03 | Points Earn Rules | P0 | 8 | 1 | Built |
| US-010-04 | Points Redemption | P0 | 8 | 2 | Built |
| US-010-05 | Points Expiry Processing | P0 | 5 | 3 | Not Started |
| US-010-06 | Points Transfer Between Members | P2 | 5 | 5 | Partial |
| US-010-07 | Reward Catalog Management | P1 | 5 | 2 | Built |
| US-010-08 | Campaign Creation (6 Types) | P0 | 8 | 2 | Built |
| US-010-09 | Communication Template Engine | P0 | 8 | 2 | Built |
| US-010-10 | Multi-Channel Campaign Dispatch | P0 | 13 | 3 | Partial |
| US-010-11 | Drip Campaign Visual Builder | P1 | 13 | 3 | Built |
| US-010-12 | Drip Campaign Execution Engine | P0 | 13 | 4 | Not Started |
| US-010-13 | Guest Segmentation (Manual) | P1 | 5 | 2 | Built |
| US-010-14 | RFM-Based Guest Segmentation | P1 | 13 | 5 | Not Started |
| US-010-15 | Feedback Survey Builder | P1 | 8 | 3 | Built |
| US-010-16 | Automated NPS Calculation | P1 | 5 | 4 | Not Started |
| US-010-17 | A/B Testing for Campaigns | P1 | 8 | 5 | Partial |
| US-010-18 | Campaign Analytics Dashboard | P1 | 8 | 4 | Partial |
| US-010-19 | WhatsApp Template Approval Workflow | P1 | 8 | 5 | Not Started |
7. Dependencies
| Dependency | Epic/Service | Type |
|---|---|---|
| Notification service (email, SMS, WhatsApp, push) | EP-014 / notification-service | Hard |
| Guest profile data | EP-003 / guest-service | Hard |
| Reservation events (Kafka) | EP-005 / reservation-service | Hard |
| Billing data for spend-based points | EP-008 / billing-service | Hard |
| Auth & RBAC for CRM roles | EP-001 / auth-service | Hard |
| SendGrid / Twilio / WhatsApp API keys | Infrastructure / Ops | Soft |
8. Acceptance Criteria (Epic-Level)
- A hotel can configure a 4-tier loyalty program with INR-based spend thresholds and points-per-rupee earn rates.
- Points expire according to configured rules (e.g., 12 months from last activity) via automated nightly cron.
- Marketing manager can create, schedule, and dispatch a campaign to a segment across email + SMS + WhatsApp in a single workflow.
- Drip campaigns execute end-to-end: trigger fires, delays respected, conditions evaluated, messages dispatched via notification service.
- A/B test splits audience randomly, tracks per-variant metrics, and auto-selects winner after configured duration.
- RFM segmentation runs nightly and classifies guests into at least 5 behavioral segments.
- NPS is auto-calculated from post-stay survey responses and displayed on CRM dashboard.
- All CRM data is tenant-isolated; no cross-tenant data leakage in queries or Kafka events.
9. KPIs
| KPI | Target | Measurement |
|---|---|---|
| Loyalty enrollment rate | > 40% of direct bookers | Enrolled members / direct bookings |
| Repeat booking rate | +15% YoY | Returning guest bookings / total |
| Campaign open rate (email) | > 25% | Opens / delivered |
| Campaign conversion rate | > 3% | Bookings from campaign / delivered |
| Points redemption rate | 30-50% of earned points | Redeemed / earned (quarterly) |
| NPS score | > 50 | Automated NPS from feedback surveys |
| Direct booking share | > 35% of total bookings | Direct / (direct + OTA) |
| RFM segment accuracy | > 80% classification accuracy | Manual audit of segment assignment |
10. Detailed User Stories
US-010-01: Define Loyalty Tiers
As a Loyalty Program Manager, I want to define multi-tier loyalty levels (Silver, Gold, Platinum, Diamond) with configurable thresholds, So that guests have clear progression goals that incentivize repeat stays.
Priority: P0 | Points: 5 | Sprint: 1 | Status: Built
Implementation Status
Loyalty tier CRUD is fully implemented in crm-service. The dashboard provides a configuration page where tiers can be created with name, minimum qualifying nights, minimum qualifying spend (INR), and tier-specific benefits. Tier badges render in guest profiles.
Acceptance Criteria
Scenario: Create a new loyalty tier
Given I am logged in as a Loyalty Program Manager for tenant "00000000-0000-4000-a000-000000000001"
When I create a loyalty tier with name "Gold", minimum nights 10, minimum spend 50000 INR, and benefits ["Room upgrade", "Late check-out"]
Then the tier is saved with status "Active"
And the tier appears in the loyalty program configuration ordered by threshold
Scenario: Prevent duplicate tier names within a tenant
Given a loyalty tier "Gold" already exists for my tenant
When I attempt to create another tier named "Gold"
Then the system returns a 409 Conflict error with message "Tier name already exists"
Scenario: Edit tier thresholds
Given the "Platinum" tier has minimum spend 100000 INR
When I update the minimum spend to 150000 INR
Then existing members at Platinum are retained until next qualification review
And new qualifications require 150000 INR spend
US-010-02: Member Enrollment
As a Front Desk Agent, I want to enroll a guest into the loyalty program during check-in using their Aadhaar or phone number, So that they start earning points from their first stay.
Priority: P0 | Points: 5 | Sprint: 1 | Status: Built
Implementation Status
Enrollment is implemented via both dashboard (front desk) and booking engine (self-enrollment). The crm-service creates a loyalty member record linked to the guest profile. Members start at the base tier (Silver). Aadhaar number is stored encrypted per Indian data protection requirements.
Acceptance Criteria
Scenario: Enroll guest at check-in
Given a guest "Rajesh Kumar" is checking in with phone "+91-9876543210"
When the front desk agent clicks "Enroll in Loyalty" and confirms
Then a loyalty membership is created with tier "Silver" and 0 points
And the guest receives a welcome SMS with membership number
And a Kafka event "loyalty.member.enrolled" is published
Scenario: Prevent duplicate enrollment
Given "Rajesh Kumar" already has loyalty membership "HMP-LY-00042"
When the agent attempts to enroll the same guest again
Then the system displays "Guest is already a loyalty member" with existing membership details
Scenario: Self-enrollment via booking engine
Given a guest is completing a booking on the booking engine
When they check "Join Loyalty Program" and provide their mobile number
Then a membership is created after booking confirmation
And the confirmation page shows their new membership number and Silver tier badge
US-010-03: Points Earn Rules
As a Loyalty Program Manager, I want to configure points earn rules based on room spend, F&B spend, and special activities, So that guests accumulate points proportional to their engagement.
Priority: P0 | Points: 8 | Sprint: 1 | Status: Built
Implementation Status
Earn rules are configurable per tenant. Three rule types supported: stay-based (points per night), spend-based (points per INR spent), and activity-based (bonus points for specific actions like completing a survey or booking direct). Rules can be tier-multiplied (e.g., Gold earns 1.5x). Kafka consumer listens for billing.invoice.finalized to auto-credit points.
Acceptance Criteria
Scenario: Earn points on room charges
Given the earn rule is "1 point per 100 INR on room charges" and Gold tier has 1.5x multiplier
And guest "Priya Sharma" is a Gold member checking out with room charges of 15000 INR
When the invoice is finalized and "billing.invoice.finalized" Kafka event fires
Then 225 points are credited to Priya's account (15000/100 * 1.5)
And a "loyalty.points.earned" event is published with breakdown
Scenario: Earn bonus points for direct booking
Given an activity rule awards 50 bonus points for direct channel bookings
When a loyalty member completes a booking through the booking engine (source: "direct")
Then 50 bonus points are credited in addition to any stay-based points
Scenario: Exclude non-qualifying charges from points calculation
Given the earn rule excludes GST and service charges
When a Gold member has a bill of 10000 INR room + 1800 INR GST (18%)
Then points are calculated on 10000 INR only, not on 11800 INR
US-010-04: Points Redemption
As a Guest, I want to redeem my loyalty points for rewards during booking or at the property, So that I receive tangible value from my loyalty membership.
Priority: P0 | Points: 8 | Sprint: 2 | Status: Built
Implementation Status
Redemption engine is built in crm-service. Supports redemption against room nights (points-per-night rate), F&B credits, and catalog rewards. Front desk can process redemptions at check-in/check-out. Booking engine has a redemption flow (currently using mock API). Minimum balance and maximum redemption percentage rules enforced.
Acceptance Criteria
Scenario: Redeem points for a room night discount
Given member "Anita Desai" has 5000 points and the redemption rate is 500 points = 1000 INR discount
When she redeems 2500 points during booking for a 6000 INR/night room
Then a discount of 5000 INR is applied to the booking total
And her points balance reduces to 2500
And the invoice line item shows "Loyalty Redemption: -5000 INR"
Scenario: Enforce minimum points balance
Given the minimum balance rule is 500 points
And member has 1200 points
When they attempt to redeem 900 points
Then the maximum redeemable is shown as 700 points (1200 - 500 minimum)
Scenario: Redeem for a catalog reward at front desk
Given the reward catalog includes "Spa 60-min massage" at 3000 points
When the front desk agent processes redemption for this reward
Then 3000 points are deducted and a spa voucher code is generated
And the voucher is sent via SMS to the member's registered mobile
US-010-05: Points Expiry Processing
As a Loyalty Program Manager, I want points to automatically expire after a configurable inactivity period, So that the hotel's loyalty liability is bounded and members are incentivized to stay active.
Priority: P0 | Points: 5 | Sprint: 3 | Status: Not Started
Implementation Status
Not implemented. The loyalty_points table has expiresAt column in the Prisma schema, but no cron job or scheduled task processes expiry. Points currently accumulate indefinitely, creating unbounded financial liability on the hotel's books.
Acceptance Criteria
Scenario: Expire points after 12 months of inactivity
Given the expiry rule is "points expire 12 months after last qualifying activity"
And member "Vikram Singh" last earned points on 2025-03-01 and has 3200 points
When the nightly expiry cron runs on 2026-03-02
Then 3200 points are marked as expired with reason "Inactivity > 12 months"
And a Kafka event "loyalty.points.expired" is published
And the member receives an email/SMS notification about expired points
Scenario: Activity resets expiry clock
Given member "Vikram Singh" has points expiring on 2026-03-01
When he completes a stay on 2026-02-15 and earns new points
Then all existing points' expiry dates are extended by 12 months from 2026-02-15
And the new expiry date is 2027-02-15
Scenario: Pre-expiry reminder notification
Given points are configured to expire after 12 months
When a member's points are 30 days from expiry
Then the system sends a reminder via email and SMS with points balance and expiry date
And the message includes a CTA to "Book Now" to keep points active
US-010-06: Points Transfer Between Members
As a Guest, I want to transfer loyalty points to another member (e.g., a family member), So that we can pool points for a shared reward.
Priority: P2 | Points: 5 | Sprint: 5 | Status: Partial
Implementation Status
The API endpoint exists (POST /crm/loyalty/transfer) and basic validation is in place (both members must belong to same tenant). However, transfer fees, daily/monthly transfer limits, and audit trail are incomplete. The dashboard UI for transfers is not built.
Acceptance Criteria
Scenario: Transfer points between family members
Given member "Meera Patel" has 8000 points and wants to transfer 3000 to member "Rohan Patel"
When she initiates a transfer via the booking engine loyalty dashboard
Then 3000 points are deducted from Meera's balance
And 3000 points (minus 10% transfer fee = 2700) are credited to Rohan's balance
And both members receive SMS confirmation of the transfer
Scenario: Enforce transfer limits
Given the monthly transfer limit is 5000 points per member
And "Meera Patel" has already transferred 3000 points this month
When she attempts to transfer another 3000 points
Then the system rejects the transfer with "Monthly transfer limit exceeded. Remaining: 2000 points"
Scenario: Transfer requires recipient verification
Given "Meera Patel" initiates a transfer to membership number "HMP-LY-00099"
When the system sends an OTP to the recipient's registered mobile
And the recipient confirms with the correct OTP
Then the transfer is processed
US-010-07: Reward Catalog Management
As a Loyalty Program Manager, I want to maintain a catalog of rewards with points costs, availability, and categories, So that members have attractive redemption options.
Priority: P1 | Points: 5 | Sprint: 2 | Status: Built
Implementation Status
Reward catalog CRUD is fully implemented. Categories include Room Upgrades, F&B Vouchers, Spa & Wellness, Experiences, and Merchandise. Each reward has a points cost, description, availability window, and maximum redemptions per period. Catalog is displayed in the booking engine loyalty dashboard and the staff dashboard.
Acceptance Criteria
Scenario: Add a reward to the catalog
Given I am a Loyalty Program Manager
When I create a reward "Complimentary Breakfast Buffet" in category "F&B Vouchers" with cost 1500 points, available daily, max 10 per day
Then the reward appears in the active catalog
And it is visible to loyalty members on the booking engine
Scenario: Disable a reward temporarily
Given the "Pool-side Dinner" reward is active
When I set its status to "Paused" for dates 2026-04-01 to 2026-04-15 (renovation period)
Then the reward is hidden from member-facing catalog during those dates
And existing reservations using this reward are not affected
Scenario: Track redemption inventory
Given "Suite Upgrade" has max 2 redemptions per day
And 2 members have already redeemed it today
When a third member attempts to redeem
Then the reward shows "Unavailable today" and suggests the next available date
US-010-08: Campaign Creation (6 Types)
As a Marketing Manager, I want to create campaigns of different types (promotional, transactional, feedback, newsletter, birthday, anniversary), So that I can engage guests with contextually relevant communications.
Priority: P0 | Points: 8 | Sprint: 2 | Status: Built
Implementation Status
All 6 campaign types are implemented in crm-service with corresponding UI in the dashboard CRM module. Each type has specific configuration options (e.g., birthday campaigns auto-select guests with birthdays in a date range, transactional campaigns link to specific Kafka events). Campaign scheduling supports immediate, scheduled, and recurring dispatch.
Acceptance Criteria
Scenario: Create a birthday campaign with auto-targeting
Given I am a Marketing Manager
When I create a campaign of type "Birthday" with template "birthday-wish-v2" and set it to run daily
Then the system auto-targets loyalty members whose birthday falls within the next 7 days
And each member receives the message on their actual birthday at 09:00 IST
Scenario: Create a promotional campaign for Diwali season
Given I select campaign type "Promotional" and choose segment "High-Value Guests (Gold+)"
When I set the subject "Diwali Special: 25% off suites", schedule for 2026-10-15 10:00 IST, and select channels Email + WhatsApp
Then the campaign is saved in "Scheduled" status
And a preview shows the estimated audience size and channel breakdown
Scenario: Create a post-stay feedback campaign
Given I select campaign type "Feedback" and link it to Kafka event "guest.checkedOut"
When I configure a 2-hour delay after check-out
Then the system sends the feedback survey link 2 hours after each guest checks out
And the campaign dashboard shows send count, response count, and average rating
US-010-09: Communication Template Engine
As a Marketing Manager, I want to create and manage message templates with dynamic variables and multi-language support, So that communications are personalized and consistent.
Priority: P0 | Points: 8 | Sprint: 2 | Status: Built
Implementation Status
Template engine is built with support for variables ({{guest_name}}, {{booking_id}}, {{check_in_date}}, {{hotel_name}}, {{loyalty_points}}, etc.). Templates are channel-specific (email HTML, SMS plain text, WhatsApp structured). Hindi and English templates exist for core transactional messages. Template versioning is supported.
Acceptance Criteria
Scenario: Create a template with variable substitution
Given I create an email template "booking-confirmation-v3"
And I include variables {{guest_name}}, {{booking_id}}, {{check_in_date}}, {{room_type}}, {{total_amount_inr}}
When the template is used for guest "Sunita Reddy" with booking "BK-20260305-001"
Then the rendered output replaces all variables with actual values
And {{total_amount_inr}} displays as "12,500" (Indian number formatting)
Scenario: Validate template before activation
Given a template references variable {{invalid_var}} which is not in the system variable list
When I attempt to save the template
Then a warning is displayed: "Unknown variable: {{invalid_var}}. This will render as empty."
And I can choose to proceed or fix the variable
Scenario: Multi-language template fallback
Given a WhatsApp template exists in Hindi ("hi") and English ("en")
And guest "Lakshmi Iyer" has language preference "ta" (Tamil)
When the system renders the template for this guest
Then it falls back to English ("en") since Tamil version is not available
And the fallback is logged for the marketing team to review
US-010-10: Multi-Channel Campaign Dispatch
As a Marketing Manager, I want to dispatch campaigns across email, SMS, WhatsApp, and push notifications from a single interface, So that guests receive messages on their preferred channel.
Priority: P0 | Points: 13 | Sprint: 3 | Status: Partial
Implementation Status
Campaign dispatch is implemented for email (via SendGrid) and SMS (via Twilio) channels. Push notification dispatch works via Expo. WhatsApp dispatch is scaffolded but blocked on Meta Business verification and template approval (see US-010-19). Channel preference per guest is stored but not yet used for automatic channel selection; dispatchers currently send to all selected channels.
Acceptance Criteria
Scenario: Dispatch a multi-channel campaign
Given a campaign targets 500 guests with channels Email + SMS
When the Marketing Manager clicks "Send Now"
Then the crm-service publishes 500 Kafka messages to "campaign.dispatch" topic
And the notification-service processes each message, sending email via SendGrid and SMS via Twilio
And the campaign status updates to "Sending" then "Sent" with counts
Scenario: Respect guest channel preferences
Given guest "Amit Joshi" has opted out of SMS but allows Email and Push
When a campaign configured for Email + SMS + Push is dispatched
Then Amit receives the message via Email and Push only
And the campaign analytics show 1 SMS suppressed due to opt-out
Scenario: Handle delivery failures gracefully
Given a campaign sends SMS to "+91-9000000000" which is an invalid number
When Twilio returns a delivery failure
Then the notification-service logs the failure with error code
And the campaign analytics increment "Failed" count
And no retry is attempted for permanent failures (error code 30003)
US-010-11: Drip Campaign Visual Builder
As a Marketing Manager, I want to design multi-step automated workflows using a visual drag-and-drop builder, So that I can create sophisticated nurture sequences without developer help.
Priority: P1 | Points: 13 | Sprint: 3 | Status: Built
Implementation Status
The drip campaign builder is fully implemented in the dashboard using a node-based React Flow editor. Supported node types: Trigger (Kafka event, date-based, manual), Delay (minutes/hours/days), Condition (guest attribute, segment membership, previous interaction), Action (send email, send SMS, send WhatsApp, add tag, update segment), and Split (A/B or conditional). Workflows are serialized as JSON and stored in the drip_campaigns table.
Acceptance Criteria
Scenario: Build a post-booking nurture drip
Given I open the drip campaign builder
When I create a workflow: Trigger("booking.confirmed") -> Delay(1 day) -> Action("Send pre-arrival email") -> Delay(check-in minus 1 day) -> Action("Send welcome SMS") -> Condition("Is loyalty member?") -> [Yes: Action("Send loyalty benefits email")] / [No: Action("Send enrollment invite")]
Then the workflow JSON is saved with all nodes and edges
And the visual preview shows the complete flow with node labels
Scenario: Validate drip workflow before activation
Given a drip workflow has an Action node with no template assigned
When I click "Activate"
Then validation fails with "Action node 'Send pre-arrival email' has no template selected"
And the problematic node is highlighted in red
Scenario: Clone an existing drip campaign
Given a drip campaign "Post-Stay Feedback Flow" exists
When I click "Clone"
Then a copy is created with name "Post-Stay Feedback Flow (Copy)" in Draft status
And all nodes, edges, and configurations are duplicated
US-010-12: Drip Campaign Execution Engine
As a Marketing Manager, I want drip campaigns to execute automatically based on triggers and conditions, So that guests receive the right message at the right time without manual intervention.
Priority: P0 | Points: 13 | Sprint: 4 | Status: Not Started
Implementation Status
Not implemented. The drip workflow JSON is saved and rendered in the UI, but there is no execution engine that processes the workflow graph. No Kafka consumer listens for trigger events to initiate drip instances. No scheduler manages delay nodes. The notification service integration for drip actions is absent.
Acceptance Criteria
Scenario: Execute a triggered drip campaign end-to-end
Given an active drip campaign triggers on "booking.confirmed" with flow: Trigger -> Delay(1 hour) -> Action("Send booking tips email")
When guest "Neha Gupta" confirms booking "BK-20260305-042"
Then a drip instance is created for Neha linked to this booking
And after 1 hour, the execution engine fires the "Send booking tips email" action
And the notification-service delivers the email with Neha's booking details substituted
Scenario: Evaluate condition nodes correctly
Given a drip has a condition: "Is guest tier Gold or above?"
And guest "Rajiv Mehta" is a Platinum member
When the execution reaches the condition node
Then the engine evaluates Rajiv's tier as Platinum (above Gold)
And proceeds down the "Yes" branch
Scenario: Handle drip instance for cancelled booking
Given a drip instance is running for booking "BK-20260305-042"
When the booking is cancelled and "booking.cancelled" event fires
Then the drip instance is terminated with status "Cancelled"
And no further messages are sent for this instance
US-010-13: Guest Segmentation (Manual)
As a Marketing Manager, I want to create manual guest segments based on tags, booking history, and demographics, So that I can target campaigns to specific guest groups.
Priority: P1 | Points: 5 | Sprint: 2 | Status: Built
Implementation Status
Manual segmentation is fully built. Segments can be defined using filters: guest tags, booking count, total spend range (INR), last stay date range, source channel, loyalty tier, city, and nationality. Segment membership is computed at query time (dynamic) or saved as a static list. Segments are used as audience targets in campaigns.
Acceptance Criteria
Scenario: Create a segment for high-value repeat guests
Given I create a segment "High-Value Repeaters" with criteria: total spend > 200000 INR AND booking count >= 3 AND last stay within 6 months
When the segment is saved and membership is computed
Then the dashboard shows the matching guest count
And I can use this segment as a campaign audience
Scenario: Dynamic segment updates automatically
Given segment "Recent Guests" filters on "last stay within 30 days"
When a new guest checks out today
Then querying the segment tomorrow includes this guest
And guests whose last stay was 31 days ago are excluded
Scenario: Export segment members
Given segment "Gold Tier - Mumbai" has 142 members
When I click "Export" and select CSV format
Then a CSV file downloads with columns: Name, Email, Phone, Tier, Total Spend, Last Stay Date
And phone numbers are masked as "+91-98XXXX3210" for data protection
US-010-14: RFM-Based Guest Segmentation
As a Marketing Manager, I want the system to automatically segment guests using RFM (Recency, Frequency, Monetary) analysis, So that I can identify champions, at-risk guests, and hibernating guests for targeted campaigns.
Priority: P1 | Points: 13 | Sprint: 5 | Status: Not Started
Implementation Status
Not implemented. The segmentation module supports manual filter-based segments only. RFM scoring requires a nightly batch job that calculates R, F, M scores (1-5 scale) for each guest based on reservation and billing data, then maps score combinations to named segments (Champions, Loyal, At Risk, Lost, etc.).
Acceptance Criteria
Scenario: Nightly RFM calculation
Given the RFM cron runs at 02:00 IST nightly
And guest "Deepa Nair" last stayed 15 days ago (Recency=5), has 8 stays in 12 months (Frequency=5), total spend 450000 INR (Monetary=5)
When the RFM job completes
Then Deepa is assigned to segment "Champions" with RFM score "5-5-5"
And the segment membership is stored in the `guest_rfm_scores` table with timestamp
Scenario: Identify at-risk guests
Given guest "Suresh Rao" last stayed 180 days ago (Recency=2), has 4 stays (Frequency=3), total spend 120000 INR (Monetary=3)
When the RFM job runs
Then Suresh is assigned to "At Risk" segment with RFM score "2-3-3"
And a Kafka event "crm.segment.atRisk" is published for potential win-back campaign trigger
Scenario: RFM thresholds are configurable per tenant
Given tenant "Luxury Resort Chain" configures Monetary score 5 threshold as > 500000 INR
And tenant "Budget Hotel" configures Monetary score 5 threshold as > 50000 INR
When both tenants' RFM jobs run
Then each tenant's guests are scored according to their own thresholds
US-010-15: Feedback Survey Builder
As a Marketing Manager, I want to create custom feedback surveys with different question types, So that we collect structured guest feedback for continuous improvement.
Priority: P1 | Points: 8 | Sprint: 3 | Status: Built
Implementation Status
Survey builder is fully implemented. Supports question types: rating (1-5 stars), NPS (0-10 scale), multiple choice, free text, and yes/no. Surveys can be linked to specific touchpoints (post-stay, post-dining, post-spa). Survey responses are stored with guest and booking references. Response analytics are available on the dashboard.
Acceptance Criteria
Scenario: Create a post-stay feedback survey
Given I create a survey "Post-Stay Feedback Q1 2026" with questions:
| Type | Question |
| NPS | How likely are you to recommend us? (0-10) |
| Rating | Room cleanliness |
| Rating | Staff friendliness |
| MCQ | Purpose of visit: Business / Leisure / Event|
| Text | Any additional comments? |
When I activate the survey and link it to the "Feedback" campaign type
Then guests receive the survey link 2 hours after check-out
And responses are stored with booking ID and guest ID references
Scenario: Survey response rate tracking
Given a survey was sent to 200 guests this month
And 68 guests completed the survey
When I view the survey analytics
Then the response rate shows 34%
And average ratings are displayed per question
Scenario: Anonymous survey option
Given the survey is configured as "Anonymous"
When a guest submits the survey
Then the response is stored without guest ID linkage
And the response cannot be traced back to a specific guest in the dashboard
US-010-16: Automated NPS Calculation
As a General Manager, I want the system to automatically calculate and track NPS from survey responses, So that I can monitor guest satisfaction trends without manual analysis.
Priority: P1 | Points: 5 | Sprint: 4 | Status: Not Started
Implementation Status
Not implemented. NPS questions are collected via the survey builder, but the NPS score (% Promoters - % Detractors) is not calculated automatically. Staff currently export responses to Excel and compute NPS manually. No trend tracking or alerting exists.
Acceptance Criteria
Scenario: Calculate NPS from survey responses
Given 100 guests responded to the NPS question this month:
| Score Range | Count | Category |
| 9-10 | 45 | Promoters |
| 7-8 | 30 | Passives |
| 0-6 | 25 | Detractors |
When the NPS calculation runs
Then the NPS is computed as 45% - 25% = +20
And the result is stored with period "2026-03" and displayed on the CRM dashboard
Scenario: NPS trend alert
Given last month's NPS was +45 and this month's NPS is +20
When the drop exceeds the configured threshold of 15 points
Then an alert is sent to the General Manager via email and dashboard notification
And the alert includes top negative feedback themes from Detractor responses
Scenario: NPS breakdown by guest segment
Given NPS responses are linked to guest profiles
When I view NPS analytics with filter "Loyalty Tier"
Then I see NPS per tier: Silver +15, Gold +35, Platinum +52, Diamond +68
And I can drill down to see individual responses per tier
US-010-17: A/B Testing for Campaigns
As a Marketing Manager, I want to run A/B tests on campaign elements (subject line, content, send time), So that I can optimize campaign performance with data-driven decisions.
Priority: P1 | Points: 8 | Sprint: 5 | Status: Partial
Implementation Status
The dashboard UI allows creating A/B variants (up to 3 variants + control) and viewing per-variant metrics. However, the backend does not perform random audience splitting. All recipients currently receive Variant A. The auto-winner selection logic (pick variant with best open rate after test period) is not implemented.
Acceptance Criteria
Scenario: Split audience for A/B test
Given a campaign targets a segment of 1000 guests
When I configure an A/B test with Variant A (subject: "Monsoon Sale: 30% Off") and Variant B (subject: "Your Exclusive Monsoon Discount")
And set test size to 20% (200 guests) with 50/50 split
Then 100 guests receive Variant A and 100 receive Variant B
And the remaining 800 are held until the winner is determined
Scenario: Auto-select winner after test period
Given an A/B test has been running for the configured 24-hour test period
And Variant A has 32% open rate and Variant B has 28% open rate
When the test period ends
Then Variant A is declared the winner
And the remaining 800 guests automatically receive Variant A
And the campaign report shows per-variant metrics side by side
Scenario: Statistical significance check
Given an A/B test completes with Variant A at 25.1% and Variant B at 25.3% open rate
When the system calculates statistical significance
Then it flags "Results not statistically significant (p > 0.05)"
And prompts the manager to choose manually or extend the test
US-010-18: Campaign Analytics Dashboard
As a Marketing Manager, I want to view detailed campaign performance metrics, So that I can measure ROI and optimize future campaigns.
Priority: P1 | Points: 8 | Sprint: 4 | Status: Partial
Implementation Status
Basic campaign analytics are implemented: sent count, delivered count, and open count (via SendGrid webhooks for email). Click tracking is implemented for email (tracked links). SMS delivery status is partially implemented. WhatsApp delivery/read receipts and conversion tracking (campaign -> booking) are not yet built.
Acceptance Criteria
Scenario: View campaign funnel metrics
Given campaign "Summer Getaway Promo" was sent to 2000 guests via Email
When I open the campaign analytics page
Then I see the funnel: Sent: 2000 -> Delivered: 1940 -> Opened: 520 -> Clicked: 98 -> Converted: 12
And conversion is defined as "booked within 7 days of click with promo code SUMMER26"
And revenue attributed to campaign is displayed in INR
Scenario: Compare campaign performance over time
Given I have run 3 promotional campaigns in Q1 2026
When I select "Compare Campaigns" view
Then a table shows all 3 campaigns with columns: Name, Sent, Open Rate, Click Rate, Conversion Rate, Revenue (INR)
And a chart visualizes the comparison
Scenario: Export analytics report
Given I am viewing analytics for "Diwali Campaign 2026"
When I click "Export PDF"
Then a PDF report is generated with campaign summary, funnel chart, channel breakdown, and top-performing content variants
And the report includes the hotel logo and tenant branding
US-010-19: WhatsApp Template Approval Workflow
As a Marketing Manager, I want to submit WhatsApp message templates for Meta approval and track their status, So that I can send compliant WhatsApp campaigns.
Priority: P1 | Points: 8 | Sprint: 5 | Status: Not Started
Implementation Status
Not implemented. WhatsApp Business API requires all outbound templates to be pre-approved by Meta. The current system has no integration with the WhatsApp Business API template management endpoints. Templates created in the CRM are not submitted to Meta, and no approval status tracking exists.
Acceptance Criteria
Scenario: Submit a template for Meta approval
Given I create a WhatsApp template "booking_confirmation" in category "Transactional" with body "Hello {{1}}, your booking {{2}} at {{3}} is confirmed for {{4}}. Reference: {{5}}"
When I click "Submit for Approval"
Then the template is sent to WhatsApp Business API via the template management endpoint
And the template status shows "Pending Approval" in the dashboard
And an estimated approval time of 24-48 hours is displayed
Scenario: Handle template rejection
Given template "flash_sale_promo" was submitted and Meta rejected it with reason "Promotional content must include opt-out instructions"
When the webhook receives the rejection event
Then the template status updates to "Rejected" with Meta's reason
And the Marketing Manager receives a dashboard notification with remediation guidance
And the template can be edited and resubmitted
Scenario: Use only approved templates in campaigns
Given I am creating a WhatsApp campaign
When I select a template for the campaign
Then only templates with status "Approved" are available in the dropdown
And templates with status "Pending" or "Rejected" are greyed out with their status shown