29 KiB
Email Notification Implementation Plan
📊 Current State Analysis
Existing Notification Types in System:
Based on codebase analysis, your system currently sends in-app notifications for:
| Notification Type | Current Method | Should Send Email? |
|---|---|---|
request_submitted |
In-app | ✅ YES (High) |
assignment |
In-app | ✅ YES (High) |
approval |
In-app | ✅ YES (High) |
rejection |
In-app | ✅ YES (Critical) |
workflow_paused |
In-app | ⚠️ CONDITIONAL |
workflow_resumed |
In-app | ✅ YES (High) |
pause_retrigger_request |
In-app | ⚠️ CONDITIONAL |
pause_retriggered |
In-app | ❌ NO (In-app only) |
tat_reminder (50%) |
In-app | ✅ YES (Medium) |
tat_reminder (75%) |
In-app | ✅ YES (High) |
tat_breach (100%) |
In-app | ✅ YES (Critical) |
mention (work notes) |
In-app | ❌ NO (In-app only) |
comment |
In-app | ❌ NO (In-app only) |
status_change |
In-app | ❌ NO (In-app only) |
document_added |
In-app | ❌ NO (In-app only) |
ai_conclusion_generated |
In-app | ❌ NO (In-app only) |
summary_generated |
In-app | ❌ NO (In-app only) |
approval_pending_closure |
In-app | ⚠️ CONDITIONAL |
auto-resume-workflow |
In-app | ❌ NO (System) |
closed |
In-app | ✅ YES (Low) |
🎯 Email vs In-App Notification Strategy
✅ SEND EMAIL (High Impact, Action Required)
Criteria:
- Requires user action
- Time-sensitive
- Critical for workflow progress
- User might not be logged in
Scenarios:
- Request Created → Send to Initiator (confirmation)
- Approval Request → Send to Approver (action required)
- Request Approved → Send to Initiator (important update)
- Request Rejected → Send to Initiator (critical)
- TAT Reminder 50% → Send to Approver (early warning)
- TAT Reminder 75% → Send to Approver (urgent)
- TAT Breached 100% → Send to Approver + Management (critical)
- Workflow Resumed → Send to Current Approver (action needed)
- Request Closed → Send to All Participants (summary)
❌ IN-APP ONLY (Low Impact, Real-time)
Criteria:
- Real-time collaboration
- Minor updates
- User likely logged in
- Frequent notifications
Scenarios:
- @Mentions in Work Notes → In-app only
- Comments Added → In-app only
- Documents Uploaded → In-app only
- Status Changes → In-app only
- AI Conclusion Generated → In-app only
- Summary Generated → In-app only
- Pause Retriggered → In-app only
⚠️ CONDITIONAL (Based on Settings)
Scenarios:
- Workflow Paused → Email if paused > 24 hours
- Participant Added → Email if user preference enabled
- Approver Skipped → Email to skipped approver only
📋 Template Mapping to Scenarios
Critical Priority Emails (Send Immediately)
| Scenario | Template | Trigger Point | Recipients |
|---|---|---|---|
| TAT Breached | getTATBreachedEmail() |
TAT timer breach | Approver, Management |
| Request Rejected | getRejectionNotificationEmail() |
Approver rejects | Initiator, Spectators |
High Priority Emails (Send within 1 minute)
| Scenario | Template | Trigger Point | Recipients |
|---|---|---|---|
| Request Created | getRequestCreatedEmail() |
Workflow created | Initiator |
| Approval Request | getApprovalRequestEmail() or getMultiApproverRequestEmail() |
Level assigned | Approver |
| Request Approved | getApprovalConfirmationEmail() |
Approver approves | Initiator |
| TAT Reminder | getTATReminderEmail() |
80% TAT elapsed | Approver |
| Workflow Resumed | getWorkflowResumedEmail() |
Workflow resumed | Approver, Initiator |
Medium Priority Emails (Send within 5 minutes)
| Scenario | Template | Trigger Point | Recipients |
|---|---|---|---|
| Workflow Paused (>24h) | getWorkflowPausedEmail() |
Long pause | Approver, Initiator |
| Participant Added | getParticipantAddedEmail() |
User preference | New participant |
| Approver Skipped | getApproverSkippedEmail() |
Skip action | Skipped approver |
Low Priority Emails (Batch send)
| Scenario | Template | Trigger Point | Recipients |
|---|---|---|---|
| Request Closed | getRequestClosedEmail() |
Workflow closed | All participants |
🔐 CRITICAL: Email Preference Control
Two-Level Preference System:
┌─────────────────────────────────────┐
│ Email Notification Request │
└──────────────┬──────────────────────┘
│
▼
┌──────────────┐
│ Admin Level │
│ Enabled? │ ──NO──> ❌ Don't send (admin disabled)
└──────┬───────┘
│ YES
▼
┌──────────────┐
│ User Level │
│ Enabled? │ ──NO──> ❌ Don't send (user disabled)
└──────┬───────┘
│ YES
▼
┌──────────────┐
│ ✅ Send Email │
└───────────────┘
IMPORTANT: Activity logs are ALWAYS captured regardless of preferences!
Preference Logic:
// Check before sending ANY email
const shouldSend = await shouldSendEmail(userId, EmailNotificationType.APPROVAL_REQUEST);
if (shouldSend) {
await emailService.sendEmail(...);
} else {
logger.info('Email skipped due to preferences');
}
// Activity ALWAYS logged regardless
await activityService.log({
type: 'assignment',
// ... activity data
});
Critical Emails (Override User Preference):
Some emails are TOO important to be disabled by users:
- ✅ Request Rejected - User must know
- ✅ TAT Breached - Critical escalation
For these, only admin can disable (user preference ignored):
if (CRITICAL_EMAILS.includes(emailType)) {
// Check admin only, ignore user preference
const adminEnabled = await isAdminEmailEnabled(emailType);
if (adminEnabled) return true;
}
🔧 Implementation Architecture
Phase 0: Preference System (REQUIRED FIRST)
Create preference checking before ANY email:
A. Email Preferences Helper (emailPreferences.helper.ts) ✅ Created
B. System Config Table:
-- Admin-level email controls
INSERT INTO system_configs (config_key, config_value, description) VALUES
('email.enabled', 'true', 'Global email notifications enabled/disabled'),
('email.request_created.enabled', 'true', 'Request created emails'),
('email.approval_request.enabled', 'true', 'Approval request emails'),
('email.request_approved.enabled', 'true', 'Approval confirmation emails'),
('email.request_rejected.enabled', 'true', 'Rejection emails (critical)'),
('email.tat_reminder_50.enabled', 'true', 'TAT 50% reminder emails'),
('email.tat_reminder_75.enabled', 'true', 'TAT 75% reminder emails'),
('email.tat_breached.enabled', 'true', 'TAT breach emails (critical)'),
('email.workflow_resumed.enabled', 'true', 'Workflow resumed emails'),
('email.request_closed.enabled', 'true', 'Request closed emails');
C. User Preferences Table:
// UserPreference.preferences column (JSONB)
{
"email": {
"enabled": true, // Global email preference
"request_created": true,
"approval_request": true,
"request_approved": true,
"request_rejected": true, // Can be overridden for critical
"tat_reminder_50": true,
"tat_reminder_75": true,
"tat_breached": true, // Can be overridden for critical
"workflow_resumed": true,
"request_closed": false // User can opt-out of closure emails
},
"notification": {
"enabled": true,
"mention": true,
"comment": true,
"assignment": true,
"status_change": true
}
}
Phase 1: Email Service Creation
Create email.service.ts:
import nodemailer from 'nodemailer';
import {
getRequestCreatedEmail,
getApprovalRequestEmail,
// ... all template functions
} from '@/emailtemplates';
export class EmailService {
private transporter: nodemailer.Transporter;
constructor() {
this.transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT || '587'),
secure: process.env.SMTP_SECURE === 'true',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASSWORD
}
});
}
async sendEmail(to: string, subject: string, html: string): Promise<void> {
try {
await this.transporter.sendMail({
from: process.env.EMAIL_FROM,
to,
subject,
html
});
logger.info(`✅ Email sent to ${to}: ${subject}`);
} catch (error) {
logger.error(`❌ Failed to send email to ${to}:`, error);
throw error;
}
}
}
export const emailService = new EmailService();
Phase 2: Email Notification Service
Create emailNotification.service.ts:
import { emailService } from './email.service';
import {
getRequestCreatedEmail,
getApprovalRequestEmail,
getMultiApproverRequestEmail,
getApprovalConfirmationEmail,
getRejectionNotificationEmail,
getTATReminderEmail,
getTATBreachedEmail,
getWorkflowPausedEmail,
getWorkflowResumedEmail,
getParticipantAddedEmail,
getApproverSkippedEmail,
getRequestClosedEmail,
getViewDetailsLink
} from '@/emailtemplates';
export class EmailNotificationService {
/**
* Send request created email to initiator
*/
async sendRequestCreated(request: any, initiator: any, firstApprover: any) {
const data = {
recipientName: initiator.name,
requestId: request.requestNumber,
requestTitle: request.title,
initiatorName: initiator.name,
firstApproverName: firstApprover.name,
requestType: request.type,
priority: request.priority,
requestDate: formatDate(request.createdAt),
requestTime: formatTime(request.createdAt),
totalApprovers: request.totalApprovers,
expectedTAT: request.tatHours,
viewDetailsLink: getViewDetailsLink(request.requestNumber),
companyName: 'Royal Enfield'
};
const html = getRequestCreatedEmail(data);
await emailService.sendEmail(
initiator.email,
`[${request.requestNumber}] Request Created Successfully`,
html
);
}
/**
* Send approval request email to approver
*/
async sendApprovalRequest(request: any, approver: any, initiator: any, isMultiLevel: boolean) {
if (isMultiLevel) {
// Use multi-approver template with approval chain
const data = {
recipientName: approver.name,
requestId: request.requestNumber,
approverName: approver.name,
initiatorName: initiator.name,
requestType: request.type,
requestDescription: request.description,
priority: request.priority,
requestDate: formatDate(request.createdAt),
requestTime: formatTime(request.createdAt),
approverLevel: approver.levelNumber,
totalApprovers: request.totalApprovers,
approversList: buildApprovalChain(request.approvalLevels, approver.levelNumber),
viewDetailsLink: getViewDetailsLink(request.requestNumber),
companyName: 'Royal Enfield'
};
const html = getMultiApproverRequestEmail(data);
await emailService.sendEmail(
approver.email,
`[${request.requestNumber}] Multi-Level Approval Request - Your Turn`,
html
);
} else {
// Use single approver template
const data = {
recipientName: approver.name,
requestId: request.requestNumber,
approverName: approver.name,
initiatorName: initiator.name,
requestType: request.type,
requestDescription: request.description,
priority: request.priority,
requestDate: formatDate(request.createdAt),
requestTime: formatTime(request.createdAt),
viewDetailsLink: getViewDetailsLink(request.requestNumber),
companyName: 'Royal Enfield'
};
const html = getApprovalRequestEmail(data);
await emailService.sendEmail(
approver.email,
`[${request.requestNumber}] Approval Request - Action Required`,
html
);
}
}
// ... Add methods for other email types
}
export const emailNotificationService = new EmailNotificationService();
Phase 3: Integration Points
Update existing services to send emails alongside in-app notifications:
A. workflow.service.ts - createWorkflow()
// After creating workflow (around line 2362)
import { shouldSendEmail, shouldSendInAppNotification, EmailNotificationType } from '@/emailtemplates/emailPreferences.helper';
// Check preferences before sending in-app notification
const sendInAppToInitiator = await shouldSendInAppNotification(
initiatorId,
'request_submitted'
);
if (sendInAppToInitiator) {
await notificationService.sendToUsers([initiatorId], {
title: 'Request Submitted Successfully',
body: `Your request "${workflowData.title}" has been submitted...`,
type: 'request_submitted',
priority: 'MEDIUM'
});
}
// Check preferences before sending email
const sendEmailToInitiator = await shouldSendEmail(
initiatorId,
EmailNotificationType.REQUEST_CREATED
);
if (sendEmailToInitiator) {
await emailNotificationService.sendRequestCreated(
workflow,
initiator,
firstApprover
);
}
// APPROVER NOTIFICATION
const sendInAppToApprover = await shouldSendInAppNotification(
approverId,
'assignment'
);
if (sendInAppToApprover) {
await notificationService.sendToUsers([approverId], {
title: 'New Request Assigned',
type: 'assignment',
priority: 'HIGH',
actionRequired: true
});
}
// Check preferences before sending email to approver
const sendEmailToApprover = await shouldSendEmail(
approverId,
EmailNotificationType.APPROVAL_REQUEST
);
if (sendEmailToApprover) {
const isMultiLevel = approvalLevels.length > 1;
await emailNotificationService.sendApprovalRequest(
workflow,
approver,
initiator,
isMultiLevel
);
}
// ACTIVITY LOG - Always captured regardless of preferences
await activityService.log({
requestId: workflow.requestId,
type: 'created',
user: { userId: initiatorId, name: initiatorName },
timestamp: new Date().toISOString(),
action: 'Request created',
details: `Request ${requestNumber} created and assigned to ${approverName}`
});
B. approval.service.ts - approveRequest()
// After approval (around line 134)
// EXISTING: In-app notification
await notificationService.sendToUsers([initiatorId], {
title: 'Request Approved',
type: 'approval',
priority: 'HIGH'
});
// NEW: Send email
await emailNotificationService.sendApprovalConfirmation(
request,
approver,
initiator,
isFinalApproval,
nextApprover
);
C. approval.service.ts - rejectRequest()
// After rejection (around line 513)
// EXISTING: In-app notification
await notificationService.sendToUsers([initiatorId], {
title: 'Request Rejected',
type: 'rejection',
priority: 'HIGH'
});
// NEW: Send email (CRITICAL)
await emailNotificationService.sendRejectionNotification(
request,
approver,
initiator,
rejectionReason
);
D. tatProcessor.ts - TAT Notifications
// TAT Reminder (80%)
if (type === 'threshold1') {
// In-app notification
await notificationService.sendToUsers([approverId], {...});
// Email notification
await emailNotificationService.sendTATReminder(request, approver);
}
// TAT Breached
if (type === 'breach') {
// In-app notification
await notificationService.sendToUsers([approverId], {...});
// Email notification (CRITICAL)
await emailNotificationService.sendTATBreached(
request,
approver,
managementUsers // Also notify management
);
}
E. pause.service.ts - Pause/Resume
// Workflow Paused
await notificationService.sendToUsers([...], {...});
// Email ONLY if pause > 24 hours
const pauseDuration = dayjs(resumeDate).diff(dayjs(), 'hours');
if (pauseDuration > 24) {
await emailNotificationService.sendWorkflowPaused(...);
}
// Workflow Resumed
await notificationService.sendToUsers([...], {...});
await emailNotificationService.sendWorkflowResumed(...);
🎯 Email Sending Decision Tree
┌─────────────────────────────────┐
│ Notification Triggered │
└────────────┬────────────────────┘
│
▼
┌──────────────┐
│ Is Critical? │ ──YES──> Send Email Immediately
│ (Rejection, │ (Critical Priority)
│ TAT Breach) │
└──────┬───────┘
│ NO
▼
┌──────────────┐
│ Requires │ ──YES──> Send Email
│ Action? │ (High Priority)
│ (Approval, │
│ Assignment) │
└──────┬───────┘
│ NO
▼
┌──────────────┐
│ Important │ ──YES──> Send Email
│ Update? │ (Medium Priority)
│ (Approved, │
│ Resumed) │
└──────┬───────┘
│ NO
▼
┌──────────────┐
│ Real-time │ ──YES──> In-App ONLY
│ Interaction? │ (No email)
│ (@mention, │
│ comment, │
│ status) │
└──────────────┘
📧 Email Scenarios - Detailed Implementation
Scenario 1: Request Created
When: User submits a new workflow request
Template: getRequestCreatedEmail()
Recipients: Initiator (confirmation)
Priority: High
Also Send In-App: Yes
Integration Point:
// File: services/workflow.service.ts
// Method: createWorkflow()
// Line: ~2362-2373
// Add after in-app notification
await emailNotificationService.sendRequestCreated(
workflow,
initiatorUser,
firstApproverUser
);
Scenario 2: Approval Request (Assignment)
When: Request assigned to approver
Template: getApprovalRequestEmail() OR getMultiApproverRequestEmail()
Recipients: Approver
Priority: High
Also Send In-App: Yes
Action Required: YES
Decision Logic:
- If
totalApprovers > 1→ UseMultiApproverRequesttemplate - If
totalApprovers === 1→ UseApprovalRequesttemplate
Integration Point:
// File: services/workflow.service.ts
// Method: createWorkflow()
// Line: ~2374-2396
// Add after in-app notification to approver
const isMultiLevel = approvalLevels.length > 1;
await emailNotificationService.sendApprovalRequest(
workflow,
approverUser,
initiatorUser,
isMultiLevel
);
Scenario 3: Request Approved
When: Approver approves the request
Template: getApprovalConfirmationEmail()
Recipients: Initiator
Priority: High
Also Send In-App: Yes
Integration Point:
// File: services/approval.service.ts
// Method: approveRequest()
// Line: ~134-145
// Add after in-app notification
await emailNotificationService.sendApprovalConfirmation(
workflow,
approverUser,
initiatorUser,
isFinalApproval,
nextApproverUser
);
Scenario 4: Request Rejected
When: Approver rejects the request
Template: getRejectionNotificationEmail()
Recipients: Initiator, Spectators
Priority: CRITICAL
Also Send In-App: Yes
Integration Point:
// File: services/approval.service.ts
// Method: rejectRequest()
// Line: ~513-525
// Add after in-app notification
await emailNotificationService.sendRejectionNotification(
workflow,
approverUser,
initiatorUser,
rejectionReason
);
Scenario 5: TAT Reminders (3 Levels)
When: 50%, 75%, and 100% (breach) of TAT elapsed
Template: getTATReminderEmail() (50%, 75%), getTATBreachedEmail() (100%)
Recipients: Approver
Priority: Medium (50%), High (75%), Critical (100%)
Also Send In-App: Yes
Integration Point:
// File: queues/tatProcessor.ts
// Update TAT thresholds to: 50%, 75%, 100% (breach)
// TAT 50% - Early Warning
if (elapsedPercentage >= 50 && !reminded50) {
// In-app notification
await notificationService.sendToUsers([approverId], {
title: 'TAT Reminder - 50% Elapsed',
type: 'tat_reminder_50',
priority: 'MEDIUM'
});
// Email (check preferences)
const shouldEmail = await shouldSendEmail(
approverId,
EmailNotificationType.TAT_REMINDER_50
);
if (shouldEmail) {
await emailNotificationService.sendTATReminder(
workflow,
approver,
{ threshold: 50, timeRemaining: '...' }
);
}
}
// TAT 75% - Urgent Warning
if (elapsedPercentage >= 75 && !reminded75) {
// In-app notification
await notificationService.sendToUsers([approverId], {
title: 'TAT Reminder - 75% Elapsed',
type: 'tat_reminder_75',
priority: 'HIGH'
});
// Email (check preferences)
const shouldEmail = await shouldSendEmail(
approverId,
EmailNotificationType.TAT_REMINDER_75
);
if (shouldEmail) {
await emailNotificationService.sendTATReminder(
workflow,
approver,
{ threshold: 75, timeRemaining: '...' }
);
}
}
// TAT 100% - BREACH (Critical)
if (elapsedPercentage >= 100) {
// In-app notification
await notificationService.sendToUsers([approverId], {
title: 'TAT BREACHED',
type: 'tat_breach',
priority: 'URGENT'
});
// Email (CRITICAL - check with override)
const shouldEmail = await shouldSendEmailWithOverride(
approverId,
EmailNotificationType.TAT_BREACHED
);
if (shouldEmail) {
await emailNotificationService.sendTATBreached(
workflow,
approver,
{ timeOverdue: '...' }
);
// Also notify management
const mgmtUsers = await getManagementUsers();
for (const mgmt of mgmtUsers) {
await emailNotificationService.sendTATBreached(
workflow,
mgmt,
{ timeOverdue: '...' }
);
}
}
}
Note: Activity logs are captured at ALL TAT thresholds regardless of email/notification preferences!
Scenario 6: TAT Breached
When: TAT deadline passed
Template: getTATBreachedEmail()
Recipients: Approver, Management (escalation)
Priority: CRITICAL
Also Send In-App: Yes
Integration Point:
// File: queues/tatProcessor.ts
// When: breach occurs
// Line: ~250-265
if (type === 'breach') {
// Existing in-app notification
await notificationService.sendToUsers([...]);
// Add email to approver
await emailNotificationService.sendTATBreached(
workflow,
approverUser,
tatData
);
// Also notify management
const managementUsers = await getManagementUsers();
for (const mgmt of managementUsers) {
await emailNotificationService.sendTATBreached(
workflow,
mgmt,
tatData
);
}
}
Scenario 7: Workflow Resumed
When: Paused workflow resumes (auto or manual)
Template: getWorkflowResumedEmail()
Recipients: Current Approver, Initiator
Priority: High
Also Send In-App: Yes
Integration Point:
// File: services/pause.service.ts
// Method: resumeWorkflow()
// Line: ~455-510
// Add after in-app notifications
await emailNotificationService.sendWorkflowResumed(
workflow,
approverUser,
initiatorUser,
resumedByUser,
pauseDuration
);
Scenario 8: Request Closed
When: Initiator closes the request after all approvals
Template: getRequestClosedEmail()
Recipients: All participants (approvers, spectators)
Priority: Low (can be batched)
Also Send In-App: Yes
Integration Point:
// File: controllers/conclusion.controller.ts
// Method: finalizeConclusion()
// Line: ~365-380
// Add after workflow closure
const allParticipants = await getRequestParticipants(requestId);
for (const participant of allParticipants) {
await emailNotificationService.sendRequestClosed(
workflow,
participant,
conclusionData
);
}
🚫 DO NOT Send Email For:
Real-Time Collaboration (In-App Only):
-
@Mentions in Work Notes
- Type:
mention - Why: Real-time, user is likely active
- Keep: In-app notification only
- Type:
-
Comments/Work Notes Added
- Type:
comment - Why: Frequent, real-time discussion
- Keep: In-app notification only
- Type:
-
Documents Uploaded
- Type:
document_added - Why: Minor update, user can see in activity
- Keep: In-app notification only
- Type:
-
Status Changes
- Type:
status_change - Why: Covered by other email notifications
- Keep: In-app notification only
- Type:
-
AI/Summary Generation
- Type:
ai_conclusion_generated,summary_generated - Why: Background process, not urgent
- Keep: In-app notification only
- Type:
-
System Events
- Type:
auto-resume-workflow - Why: System-triggered, covered by resume email
- Keep: In-app notification only
- Type:
🔄 Implementation Phases
Phase 1: Core Email Service (Week 1)
- Create
email.service.ts - Configure SMTP with nodemailer
- Test email sending with test account
- Add error handling and retry logic
Phase 2: Email Notification Service (Week 1-2)
- Create
emailNotification.service.ts - Implement all 12 template functions
- Add data formatting helpers
- Test with sample data
Phase 3: Critical Notifications (Week 2)
- Integrate TAT Breached emails
- Integrate Rejection emails
- Test critical scenarios
Phase 4: High Priority Notifications (Week 2-3)
- Integrate Request Created emails
- Integrate Approval Request emails
- Integrate Approval Confirmation emails
- Integrate TAT Reminder emails
- Integrate Workflow Resumed emails
Phase 5: Medium/Low Priority (Week 3)
- Integrate Workflow Paused emails (conditional)
- Integrate Participant Added emails (conditional)
- Integrate Approver Skipped emails
- Integrate Request Closed emails
Phase 6: Email Queue System (Week 4)
- Install Bull/BullMQ
- Create email queue
- Implement priority-based sending
- Add retry mechanism
- Monitor email delivery
Phase 7: User Preferences (Week 4-5)
- Add email preferences to user settings
- Allow users to opt-in/out of certain emails
- Keep critical emails always enabled
- Add digest email option (daily summary)
⚙️ Environment Configuration
Add to .env:
# Email Service
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=notifications@royalenfield.com
SMTP_PASSWORD=your-app-specific-password
EMAIL_FROM=RE Flow <noreply@royalenfield.com>
# Email Settings
EMAIL_ENABLED=true
EMAIL_QUEUE_ENABLED=true
EMAIL_BATCH_SIZE=50
EMAIL_RETRY_ATTEMPTS=3
# Application
BASE_URL=https://workflow.royalenfield.com
COMPANY_NAME=Royal Enfield
COMPANY_WEBSITE=https://www.royalenfield.com
SUPPORT_EMAIL=support@royalenfield.com
📊 Expected Email Volume
Based on typical workflow usage:
| Email Type | Frequency | Daily Volume (Est.) |
|---|---|---|
| Request Created | Per new request | 20-50 |
| Approval Request | Per assignment | 20-50 |
| Approval Confirmation | Per approval | 15-40 |
| Rejection | Occasional | 2-5 |
| TAT Reminder | 80% threshold | 5-15 |
| TAT Breached | Critical | 1-5 |
| Workflow Resumed | Occasional | 2-10 |
| Request Closed | Per closure | 10-30 |
| TOTAL | 75-205 emails/day |
No emails for @mentions, comments, documents (could be 100s per day).
✅ Success Metrics
Email Delivery:
- Delivery rate > 98%
- Bounce rate < 2%
- Open rate > 40% (approvers)
- Click rate > 25% (View Details button)
User Experience:
- Reduced missed approvals
- Faster TAT completion
- Better stakeholder awareness
- No email fatigue (not too many emails)
🚀 Next Steps
- Review this plan - Confirm scenarios and priorities
- Configure SMTP - Set up email credentials
- Start with Phase 1 - Build email service
- Test thoroughly - Use test account first
- Roll out gradually - Start with critical emails only
- Monitor metrics - Track delivery and engagement
- Gather feedback - Adjust based on user preferences
Ready to start implementation? 🎯