Skip to main content

EP-010: CRM, Loyalty & Campaigns

FieldValue
Epic IDEP-010
TitleCRM, Loyalty & Campaigns
OwnerProduct -- CRM & Guest Engagement
StatusPartial
Last Updated2026-03-05
Target ReleaseR4 (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​

RoleInteraction
Marketing ManagerCreates campaigns, segments guests, monitors analytics
Loyalty Program ManagerDefines tiers, earn/burn rules, reward catalog
Front Desk AgentEnrolls guests, redeems points at check-in/check-out
Revenue ManagerReviews campaign ROI, adjusts promotional strategies
Guest (end-user)Earns/redeems points, receives communications, fills surveys
Super AdminConfigures 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 tenantId on 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​

ComponentService / ModuleStatus
Loyalty tier CRUDcrm-service / loyalty moduleBuilt
Points earn/burn rulescrm-service / loyalty moduleBuilt
Member enrollmentcrm-service / loyalty moduleBuilt
Reward catalogcrm-service / loyalty moduleBuilt
Points expiry croncrm-service / loyalty moduleNot Started
Points transfercrm-service / loyalty modulePartial
Campaign CRUD (6 types)crm-service / campaigns moduleBuilt
Template enginecrm-service / templates moduleBuilt
Drip campaign builder UIDashboard CRM moduleBuilt
Drip execution enginecrm-service / drip moduleNot Started
Guest segmentation (manual)crm-service / segments moduleBuilt
RFM segmentationcrm-service / segments moduleNot Started
Feedback surveyscrm-service / feedback moduleBuilt
NPS calculationcrm-service / feedback moduleNot Started
A/B testing UIDashboard CRM moduleBuilt
A/B split logic (backend)crm-service / campaigns moduleNot Started
Campaign analyticscrm-service / analytics modulePartial
WhatsApp template approvalcrm-service / templates moduleNot Started
Dashboard CRM pages02-hmp-web-dashboardBuilt

5. Gap Analysis​

#GapImpactEffortPriority
1Drip campaign execution engine not connected to notification serviceDrip workflows render in UI but never fire actual messagesHighP0
2A/B testing has UI but no backend split logicCannot run real A/B experiments; all recipients get variant AMediumP1
3NPS calculation not automatedStaff must manually compute NPS from raw survey responsesMediumP1
4RFM-based segmentation not implementedSegments are manual tag-based; no data-driven guest clusteringHighP1
5Points expiry cron job not implementedPoints never expire, creating unbounded liabilityHighP0
6WhatsApp template approval workflow missingCannot send WhatsApp campaigns (Meta requires pre-approved templates)MediumP1
7Campaign delivery tracking incompleteOpen/click tracking works for email; SMS/WhatsApp delivery status not capturedMediumP2

6. User Stories Summary​

IDTitlePriorityPointsSprintStatus
US-010-01Define Loyalty TiersP051Built
US-010-02Member EnrollmentP051Built
US-010-03Points Earn RulesP081Built
US-010-04Points RedemptionP082Built
US-010-05Points Expiry ProcessingP053Not Started
US-010-06Points Transfer Between MembersP255Partial
US-010-07Reward Catalog ManagementP152Built
US-010-08Campaign Creation (6 Types)P082Built
US-010-09Communication Template EngineP082Built
US-010-10Multi-Channel Campaign DispatchP0133Partial
US-010-11Drip Campaign Visual BuilderP1133Built
US-010-12Drip Campaign Execution EngineP0134Not Started
US-010-13Guest Segmentation (Manual)P152Built
US-010-14RFM-Based Guest SegmentationP1135Not Started
US-010-15Feedback Survey BuilderP183Built
US-010-16Automated NPS CalculationP154Not Started
US-010-17A/B Testing for CampaignsP185Partial
US-010-18Campaign Analytics DashboardP184Partial
US-010-19WhatsApp Template Approval WorkflowP185Not Started

7. Dependencies​

DependencyEpic/ServiceType
Notification service (email, SMS, WhatsApp, push)EP-014 / notification-serviceHard
Guest profile dataEP-003 / guest-serviceHard
Reservation events (Kafka)EP-005 / reservation-serviceHard
Billing data for spend-based pointsEP-008 / billing-serviceHard
Auth & RBAC for CRM rolesEP-001 / auth-serviceHard
SendGrid / Twilio / WhatsApp API keysInfrastructure / OpsSoft

8. Acceptance Criteria (Epic-Level)​

  1. A hotel can configure a 4-tier loyalty program with INR-based spend thresholds and points-per-rupee earn rates.
  2. Points expire according to configured rules (e.g., 12 months from last activity) via automated nightly cron.
  3. Marketing manager can create, schedule, and dispatch a campaign to a segment across email + SMS + WhatsApp in a single workflow.
  4. Drip campaigns execute end-to-end: trigger fires, delays respected, conditions evaluated, messages dispatched via notification service.
  5. A/B test splits audience randomly, tracks per-variant metrics, and auto-selects winner after configured duration.
  6. RFM segmentation runs nightly and classifies guests into at least 5 behavioral segments.
  7. NPS is auto-calculated from post-stay survey responses and displayed on CRM dashboard.
  8. All CRM data is tenant-isolated; no cross-tenant data leakage in queries or Kafka events.

9. KPIs​

KPITargetMeasurement
Loyalty enrollment rate> 40% of direct bookersEnrolled members / direct bookings
Repeat booking rate+15% YoYReturning guest bookings / total
Campaign open rate (email)> 25%Opens / delivered
Campaign conversion rate> 3%Bookings from campaign / delivered
Points redemption rate30-50% of earned pointsRedeemed / earned (quarterly)
NPS score> 50Automated NPS from feedback surveys
Direct booking share> 35% of total bookingsDirect / (direct + OTA)
RFM segment accuracy> 80% classification accuracyManual 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