Messages Module
TLDR: Bidirectional messaging between couples and vendors with AI-powered response detection, automated reminders at 24h/72h/168h intervals, and event-driven cancellation when recipients respond.
Overview
The Messages module handles all communication between couples and vendors in the Wedissimo marketplace. It provides threaded conversations, read receipts, AI-powered response detection, and automated reminder scheduling to ensure timely vendor responses.
Primary use cases:
- Couples inquiring about vendor services
- Vendors responding to booking requests
- Follow-up reminders when messages go unanswered
- Conversation history and audit trail
Architecture
Core Models
Message
- Primary communication entity within a thread
- References sender via
sender_id(User) - UUID primary keys, soft deletes enabled
- Scanned for disintermediation attempts
MessageThread
- Groups messages between couple and vendor
- Links to
vendor_idanduser_id(the couple) - Tracks
response_needed_at,last_response_at
MessageThreadReminder
- Pre-scheduled follow-up notifications
- Bidirectional: vendor OR couple can be reminded
- AI-powered needs-response detection via Vertex AI
- Intervals: 24h, 72h, 168h (levels 1, 2, 3)
- Event-driven cancellation when recipient responds
Database Design
Message Threads Table (message_threads)
- id (uuid, primary key)
- vendor_id, user_id (foreign keys)
- subject (nullable)
- response_needed_at, last_response_at (timestamps)
- awaiting_response_to_message_id (FK)Messages Table (messages)
- id (uuid, primary key)
- thread_id (uuid, FK to message_threads)
- sender_id (uuid, FK to users)
- body (text), body_redacted (nullable)
- status (pending, approved, flagged, blocked, redacted)
- created_at, updated_at, deleted_atMessage Thread Reminders Table (message_thread_reminders)
- id (uuid, primary key)
- thread_id, trigger_message_id, recipient_id (FKs)
- recipient_type ('vendor' or 'couple')
- level (1, 2, or 3)
- scheduled_for (timestamp)
- sent_at, cancelled_at (nullable timestamps)
- cancellation_reason (nullable)Key Indexes:
reminders_due_idx: Partial indexWHERE sent_at IS NULL AND cancelled_at IS NULLreminders_cancel_idx: Compound on(thread_id, recipient_id, sent_at, cancelled_at)
Key Relationships
MessageThread
├── belongsTo: vendor (Vendor)
├── belongsTo: user (User - the couple)
├── hasMany: messages (Message)
└── hasMany: reminders (MessageThreadReminder)
Message
├── belongsTo: thread (MessageThread)
├── belongsTo: sender (User)
└── hasMany: reminders (MessageThreadReminder)
MessageThreadReminder
├── belongsTo: thread (MessageThread)
├── belongsTo: triggerMessage (Message)
└── belongsTo: recipient (User)Message Reminders System (FOU-59)
Bidirectional Reminder Logic
Vendor Reminder (vendor_no_response):
- Triggered when couple sends message to vendor
- Vendor hasn't responded within threshold
- Reminds vendor to respond to inquiry
Couple Reminder (couple_no_response):
- Triggered when vendor sends message to couple
- Couple hasn't responded within threshold
- Reminds couple to respond to vendor's answer
AI-Powered Response Detection
The system uses Google Vertex AI to determine if a message requires a response:
Why AI detection?
- Simple heuristics fail ("Thanks!" still needs vendor reply with details)
- Question marks don't always indicate questions
- Context matters: "Great, I'll book!" vs "Can you do Saturday?"
How it works:
- Message created event dispatched
- AI analyzes message content and conversation context
- If response needed: schedule reminders at 24h, 72h, 168h
- If no response needed: no reminders scheduled
Example prompts:
- "What's your pricing for 100 guests?" → Needs response
- "Thanks for the info, we'll be in touch!" → No response needed
- "Great! Can you also do photography?" → Needs response
Automated Scheduling
When AI detects a message needing response:
- 24-hour reminder: First gentle nudge
- 72-hour reminder: Second follow-up if still no response
- 168-hour reminder: Final reminder after 1 week
Event-driven cancellation:
- Recipient responds → Cancel all pending reminders for that message
- Sender deletes message → Cancel all reminders
- Thread closed → Cancel all thread reminders
Implementation Notes
Queue processing:
- AI check:
CheckMessageNeedsResponseJob- analyzes message, schedules reminders - Reminder delivery:
messages:send-reminderscommand (every 5 min) →SendSingleReminderJob - Reminder cancellation:
ParticipantRespondedToThreadevent →CancelRemindersOnResponselistener
Notification channels:
- Email (primary) via
UnrespondedMessageReminderNotification - Escalating tone: friendly (24h) → urgent (72h) → final (168h)
Key Concepts
Thread Context
Messages are grouped into threads based on:
- Listing context: Inquiry about specific vendor listing
- Package context: Questions about specific package/service
- General inquiry: Direct vendor contact without listing context
Thread context enables:
- Conversation history
- Related message grouping
- Context-aware AI analysis
Read Receipts
Read state tracking:
read_attimestamp on Message model- Updated when recipient views message
- Enables "seen" indicators in UI
Privacy considerations:
- Read receipts visible to sender
- Configurable per user (future: opt-out)
Soft Deletes
Messages use soft deletes to:
- Maintain conversation integrity
- Preserve audit trail
- Enable "undo" functionality
- Comply with data retention policies
Deletion behavior:
- Sender can delete their sent messages (soft delete)
- Recipient can delete from their inbox (soft delete)
- Hard deletes: admin only, legal compliance
Service Layer
MessageService
- Core business logic for message creation
- Dispatches
CheckMessageNeedsResponseJobafter creation - Fires
ParticipantRespondedToThreadevent
MessageReminderService
getDueReminders()- queries pending reminders (uses partial index)sendReminder()- sends with pessimistic lockingcancelPendingRemindersForRecipient()- cancels on response
VertexAiScanningService
scanForNeedsResponse()- heuristics + AI fallbackscanForDisintermediation()- contact info detectionscanForAvailability()- vendor availability detection
Events & Listeners
ParticipantRespondedToThread
- Fired when any participant sends a message
- Contains: thread, sender (User), senderType ('vendor'/'couple')
- Listened by:
CancelRemindersOnResponse
CancelRemindersOnResponse
- Cancels all pending reminders where sender was the intended recipient
- Updates thread
last_response_at
Authorization
Policy-based authorization:
- Users can only message vendors they have access to
- Vendors can only view messages for their listings
- Thread participants can view all thread messages
- Admins have full access for moderation
Permission checks:
MessagePolicy::create(): Can user send message to recipient?MessagePolicy::view(): Can user view this message?MessagePolicy::delete(): Can user delete this message?
Integration Points
Vendor Module
- Messages tied to vendor listings
- Vendor response metrics (future: response time tracking)
User Module
- Polymorphic sender/recipient relationships
- User notification preferences
Notification Module
- Email delivery for message notifications
- In-app notification badges
- SMS delivery (optional)
AI Services
- Vertex AI for response detection
- Future: sentiment analysis, auto-categorization
What's NOT in This Documentation
API endpoints: See OpenAPI specification in modules/Messages/OpenAPI/
Request/response formats: See OpenAPI schema definitions
Authentication flows: See Authentication module docs
Frontend integration: See UI component documentation
Next Steps
For developers:
- Read the Development Guide to extend the messaging system
- Review Operations Guide for troubleshooting and monitoring
For API consumers:
- Review OpenAPI documentation for endpoint details
- Check authentication requirements in Auth module docs
- See rate limiting policies in API documentation