Communication Recipient Targeting - Changes Summary
What Changes?
Current Structure (Before)
business_communication_config
├── Controls: WHAT and HOW (type + channel + timing)
└── Recipients: NONE (hardcoded in handler code)
Problem: To change recipients, you need to modify code and redeploy!
New Structure (After)
business_communication_config
├── Controls: WHAT and HOW (type + channel + timing)
└── Extended by: business_communication_recipient_rule
└── Controls: WHO (roles + groups + ad-hoc)
Solution: Business admins configure recipients via admin UI - no code changes needed!
New Tables Overview
1. communication_recipient_group
Purpose: Create custom groups of recipients
| Column | Description | Example |
|---|---|---|
| name | Group name | "Finance Team", "Purchasing Dept" |
| description | Purpose | "Team handling all financial reports" |
| is_active | Enable/disable | true/false |
Use Case: Create a "Purchasing Team" group containing 3 staff members + 1 supplier email
2. communication_group_member
Purpose: Define who's in each group
| Column | Type | Example |
|---|---|---|
| member_type | enum | business_user, email, phone, whatsapp |
| business_user_id | uuid | Link to existing user |
| email_address | varchar | external@supplier.com |
| phone_number | varchar | +1234567890 |
Use Case: Add internal users + external contacts to the same group
3. business_communication_recipient_rule
Purpose: Define WHO receives each communication type/channel
| Targeting Type | Example | What It Does |
|---|---|---|
role | "inventory_manager" | Send to ALL users with this role |
group | "Purchasing Team" | Send to ALL members in this group |
ad_hoc_email | "supplier@vendor.com" | Send to this specific email |
ad_hoc_phone | "+1234567890" | Send to this specific phone |
Use Case: Configure low_stock_alert to go to: inventory_manager role + Purchasing Team group + owner's phone
4. communication_recipient_log
Purpose: Audit trail of recipient selection
Use Case: See who was selected as recipients and why (for compliance/debugging)
Configuration Examples
Scenario 1: Low Stock Alerts
Current: Hardcoded to send to ALL business users ❌
New: Configure via rules ✅
-- Rule 1: Send to inventory managers
INSERT INTO business_communication_recipient_rule
VALUES (..., 'low_stock_alert', 'email', 'role', 'inventory_manager');
-- Rule 2: Send to purchasing team
INSERT INTO business_communication_recipient_rule
VALUES (..., 'low_stock_alert', 'email', 'group', 'purchasing-team-uuid');
-- Rule 3: SMS to owner's mobile
INSERT INTO business_communication_recipient_rule
VALUES (..., 'low_stock_alert', 'sms', 'ad_hoc_phone', '+1234567890');
Result:
- Email → All inventory managers + purchasing team members
- SMS → Owner's phone
- Configurable via admin UI (no code deploy needed!)
Scenario 2: Daily Reports
Current: Would need custom code for each business ❌
New: Each business configures their own ✅
Business A:
-- Send to finance group only
INSERT INTO business_communication_recipient_rule
VALUES ('business-A', 'daily_report', 'email', 'group', 'finance-group');
Business B:
-- Send to owner + admin + external accountant
INSERT INTO business_communication_recipient_rule
VALUES ('business-B', 'daily_report', 'email', 'role', 'owner'),
('business-B', 'daily_report', 'email', 'role', 'admin'),
('business-B', 'daily_report', 'email', 'ad_hoc_email', 'cpa@firm.com');
How It Works
Before (Current Handler)
// ❌ Hardcoded: Send to ALL business users
const businessUsers = await this.businessUsersService.findByBusinessId(businessId);
for (const user of businessUsers) {
await this.communicationsService.send({ recipientContact: user.email });
}
After (With RecipientResolverService)
// ✅ Dynamic: Resolve recipients based on business rules
const recipients = await this.recipientResolverService.resolveRecipients(
businessId,
'low_stock_alert',
'email'
);
for (const recipient of recipients) {
await this.communicationsService.send({
recipientContact: recipient.contact,
// Audit: Track why this person was selected
metadata: { selectionSource: recipient.source }
});
}
RecipientResolverService logic:
- Query all active rules for this business + type + channel
- For each rule, resolve to actual recipients:
- Role rule → Query all users with that role
- Group rule → Query all group members
- Ad-hoc rule → Use the email/phone directly
- Deduplicate (same person via multiple rules)
- Return final list of recipients
Migration Strategy
Phase 1: Database (No Breaking Changes)
✅ Run migration to create new tables ✅ Existing handlers continue to work (no changes yet)
Phase 2: Create Services
RecipientGroupRepositoryRecipientRuleRepositoryRecipientResolverService
Phase 3: Create APIs
- Group management endpoints
- Recipient rule management endpoints
- Preview endpoint (see who will receive before saving)
Phase 4: Update Handlers
Update event handlers one at a time:
- Low stock alerts (migrate first)
- Daily reports
- Invoices
- Collection notices ... etc
Phase 5: Admin UI
- Group management page
- Recipient rule configuration page
- Integration with communication settings
Key Benefits
| Benefit | Description |
|---|---|
| Flexibility | Mix roles, groups, and ad-hoc recipients |
| Self-Service | Business admins configure without developer help |
| Audit Trail | Know exactly who received what and why |
| Scalability | Add new communication types without code changes |
| Multi-Tenant | Each business has different recipient rules |
| No Code Deploy | Change recipients via admin UI, not code |
Comparison Table
| Feature | Before | After |
|---|---|---|
| Recipient Configuration | Hardcoded in handlers | Database-driven rules |
| Change Recipients | Code change + deploy | Admin UI (instant) |
| Send to Roles | ❌ No | ✅ Yes |
| Send to Groups | ❌ No | ✅ Yes |
| Send to External Contacts | ❌ No | ✅ Yes |
| Mix Multiple Types | ❌ No | ✅ Yes (roles + groups + ad-hoc) |
| Per-Business Config | ❌ No (all businesses same) | ✅ Yes (each configures their own) |
| Audit Trail | ❌ No | ✅ Yes (recipient_log table) |
| Preview Recipients | ❌ No | ✅ Yes (via API) |
Example API Calls
Create a Group
POST /api/recipient-groups
{
"businessId": "business-123",
"name": "Finance Team",
"description": "Team handling financial reports"
}
Add Members to Group
POST /api/recipient-groups/{groupId}/members
{
"members": [
{ "type": "business_user", "userId": "user-1" },
{ "type": "business_user", "userId": "user-2" },
{ "type": "email", "email": "cpa@external.com", "name": "External CPA" }
]
}
Create Recipient Rules
POST /api/communication-recipient-rules
{
"businessId": "business-123",
"communicationType": "low_stock_alert",
"channel": "email",
"rules": [
{ "targetingType": "role", "roleName": "inventory_manager" },
{ "targetingType": "group", "groupId": "group-456" },
{ "targetingType": "ad_hoc_phone", "phone": "+1234567890", "name": "Owner Mobile" }
]
}
Preview Recipients (Before Saving)
POST /api/communication-recipient-rules/preview
{
"businessId": "business-123",
"communicationType": "low_stock_alert",
"channel": "email",
"rules": [...]
}
// Response:
{
"recipients": [
{ "name": "John Doe", "contact": "john@company.com", "source": "role:inventory_manager" },
{ "name": "Jane Smith", "contact": "jane@company.com", "source": "group:Purchasing Team" },
{ "name": "External Supplier", "contact": "supplier@vendor.com", "source": "group:Purchasing Team" },
{ "name": "Owner Mobile", "contact": "+1234567890", "source": "ad_hoc" }
],
"totalCount": 4
}
Decision Matrix: When to Use What
| Scenario | Use This |
|---|---|
| Send to everyone with a specific job function | Role (e.g., all managers) |
| Send to a specific team across locations | Group (e.g., Finance Team) |
| Send to external party | Ad-hoc email/phone |
| Send to mix of internal + external | Group with mixed members |
| Send to specific person's backup contact | Ad-hoc (personal phone/email) |
| Different recipients per business | Recipient rules (per business) |
| Different recipients per channel | Multiple rules (one per channel) |
Summary
The recipient targeting system extends your existing business_communication_config with flexible recipient rules. Instead of hardcoding recipients in event handlers, businesses now configure their own rules via admin UI.
The key insight:
business_communication_config= WHAT to send and HOW to send itbusiness_communication_recipient_rule= WHO should receive it
This separation allows maximum flexibility while maintaining backward compatibility and audit trails.