32 KiB
Notification Service
Overview
The Notification Service handles multi-channel communication with bakery owners, managers, and staff through Email (SMTP) and WhatsApp (Twilio). It delivers critical operational alerts (stockouts, quality issues, equipment maintenance), business insights (daily summaries, forecast updates), and customer communications (order confirmations, delivery notifications). The service ensures that important information reaches the right people at the right time through their preferred communication channel.
Key Features
Multi-Channel Communication
- Email (SMTP) - Professional email notifications
- WhatsApp (Twilio) - Instant messaging for urgent alerts
- SMS (Twilio) - Fallback text messaging
- Channel Prioritization - Auto-select channel by urgency
- Channel Preferences - User-defined communication preferences
- Multi-Recipient - Send to individuals or groups
- Delivery Tracking - Monitor delivery status per channel
Email Capabilities
- HTML Templates - Professional branded emails
- Plain Text Fallback - Ensure compatibility
- Attachments - PDF reports, invoices, documents
- Inline Images - Logo, charts embedded
- Email Templates - Pre-designed templates per alert type
- Variable Substitution - Dynamic content per recipient
- Batch Sending - Efficient bulk email delivery
WhatsApp Integration
- Twilio API - Official WhatsApp Business API
- Rich Messages - Text, images, documents
- Message Templates - Pre-approved templates for compliance
- Interactive Messages - Quick reply buttons
- Media Support - Send images, PDFs
- Delivery Receipts - Read/delivered status
- Opt-In Management - GDPR-compliant consent tracking
Notification Types
- Critical Alerts - Stockouts, equipment failure, quality issues
- High Priority - Low stock warnings, forecast anomalies
- Medium Priority - Daily summaries, scheduled reports
- Low Priority - Weekly digests, monthly reports
- Informational - System updates, tips, best practices
- Customer Notifications - Order confirmations, delivery updates
Template Management
- Template Library - 20+ pre-built templates
- Template Versioning - Track template changes
- Multi-Language - Spanish, English, Catalan
- Variable Placeholders - Dynamic content insertion
- Template Preview - Test before sending
- Custom Templates - Create tenant-specific templates
- Template Analytics - Open rates, click rates
Delivery Management
- Queue System - Prioritized delivery queue
- Retry Logic - Automatic retry on failure
- Rate Limiting - Respect API rate limits
- Delivery Status - Track sent, delivered, failed, read
- Failure Handling - Fallback to alternative channels
- Bounce Management - Handle invalid addresses
- Unsubscribe Management - Honor opt-out requests
Analytics & Reporting
- Delivery Metrics - Success rates per channel
- Open Rates - Email open tracking
- Click Rates - Link click tracking
- Response Times - Time to read/acknowledge
- Channel Effectiveness - Compare channel performance
- Cost Analysis - Communication costs per channel
- User Engagement - Active users per channel
Business Value
For Bakery Owners
- Real-Time Alerts - Know critical issues immediately
- Channel Flexibility - Email for reports, WhatsApp for urgent
- Cost Effective - WhatsApp cheaper than SMS
- Professional Communication - Branded emails enhance reputation
- Customer Engagement - Order updates improve satisfaction
- Remote Management - Stay informed from anywhere
Quantifiable Impact
- Response Time: 90% faster with WhatsApp alerts (minutes vs. hours)
- Issue Resolution: 50-70% faster with immediate notifications
- Cost Savings: €50-150/month (WhatsApp vs. SMS or phone calls)
- Customer Satisfaction: 20-30% improvement with order updates
- Staff Efficiency: 3-5 hours/week saved on manual communication
- Alert Reliability: 99%+ delivery rate
For Operations Staff
- Instant Alerts - WhatsApp notifications on critical issues
- Actionable Information - Clear instructions in alerts
- Mobile Access - Receive alerts on phone
- Alert History - Review past notifications
- Acknowledgment - Confirm receipt of critical alerts
For Customers
- Order Confirmation - Immediate order receipt
- Delivery Updates - Know when order is ready
- Personalized Communication - Address by name
- Multiple Channels - Choose email or WhatsApp
- Professional Image - Branded communication
Technology Stack
- Framework: FastAPI (Python 3.11+) - Async web framework
- Database: PostgreSQL 17 - Notification history
- Queue: RabbitMQ 4.1 - Notification queue
- Caching: Redis 7.4 - Template cache
- Email: SMTP (SendGrid, Amazon SES, SMTP server)
- WhatsApp: Twilio API - WhatsApp Business
- SMS: Twilio API - SMS fallback
- Templates: Jinja2 - Template rendering
- Logging: Structlog - Structured JSON logging
- Metrics: Prometheus Client - Delivery metrics
API Endpoints (Key Routes)
Notification Sending
POST /api/v1/notifications/send- Send notificationPOST /api/v1/notifications/send-email- Send emailPOST /api/v1/notifications/send-whatsapp- Send WhatsAppPOST /api/v1/notifications/send-sms- Send SMSPOST /api/v1/notifications/send-batch- Bulk send
Notification Management
GET /api/v1/notifications- List notificationsGET /api/v1/notifications/{notification_id}- Get notification detailsGET /api/v1/notifications/{notification_id}/status- Check delivery statusPOST /api/v1/notifications/{notification_id}/retry- Retry failed notificationDELETE /api/v1/notifications/{notification_id}- Cancel pending notification
Template Management
GET /api/v1/notifications/templates- List templatesGET /api/v1/notifications/templates/{template_id}- Get templatePOST /api/v1/notifications/templates- Create templatePUT /api/v1/notifications/templates/{template_id}- Update templatePOST /api/v1/notifications/templates/{template_id}/preview- Preview templateDELETE /api/v1/notifications/templates/{template_id}- Delete template
User Preferences
GET /api/v1/notifications/preferences- Get user preferencesPUT /api/v1/notifications/preferences- Update preferencesPOST /api/v1/notifications/preferences/opt-out- Opt out of notificationsPOST /api/v1/notifications/preferences/opt-in- Opt in to notifications
Analytics
GET /api/v1/notifications/analytics/dashboard- Notification dashboardGET /api/v1/notifications/analytics/delivery-rates- Delivery success ratesGET /api/v1/notifications/analytics/channel-performance- Channel comparisonGET /api/v1/notifications/analytics/engagement- User engagement metrics
Webhooks
POST /api/v1/notifications/webhooks/twilio- Twilio status webhookPOST /api/v1/notifications/webhooks/sendgrid- SendGrid webhookPOST /api/v1/notifications/webhooks/ses- Amazon SES webhook
Database Schema
Main Tables
notifications
CREATE TABLE notifications (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
notification_type VARCHAR(100) NOT NULL, -- alert, report, customer, system
priority VARCHAR(50) NOT NULL, -- critical, high, medium, low
channel VARCHAR(50) NOT NULL, -- email, whatsapp, sms
status VARCHAR(50) DEFAULT 'pending', -- pending, queued, sent, delivered, failed, cancelled
-- Recipient
recipient_user_id UUID,
recipient_name VARCHAR(255),
recipient_email VARCHAR(255),
recipient_phone VARCHAR(50),
-- Content
subject VARCHAR(500),
message_body TEXT NOT NULL,
template_id UUID,
template_variables JSONB,
-- Attachments
attachments JSONB, -- Array of attachment URLs
-- Delivery
scheduled_at TIMESTAMP,
sent_at TIMESTAMP,
delivered_at TIMESTAMP,
read_at TIMESTAMP,
failed_at TIMESTAMP,
failure_reason TEXT,
retry_count INTEGER DEFAULT 0,
max_retries INTEGER DEFAULT 3,
-- External IDs
external_message_id VARCHAR(255), -- Twilio SID, SendGrid message ID
external_status VARCHAR(100),
-- Tracking
opened BOOLEAN DEFAULT FALSE,
clicked BOOLEAN DEFAULT FALSE,
open_count INTEGER DEFAULT 0,
click_count INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
INDEX idx_notifications_tenant_status (tenant_id, status),
INDEX idx_notifications_recipient (recipient_user_id),
INDEX idx_notifications_scheduled (scheduled_at) WHERE status = 'pending'
);
notification_templates
CREATE TABLE notification_templates (
id UUID PRIMARY KEY,
tenant_id UUID, -- NULL for global templates
template_name VARCHAR(255) NOT NULL,
template_code VARCHAR(100) NOT NULL, -- Unique code for reference
template_type VARCHAR(100) NOT NULL, -- alert, report, customer, system
channel VARCHAR(50) NOT NULL, -- email, whatsapp, sms, all
-- Content
subject_template TEXT,
body_template TEXT NOT NULL,
html_body_template TEXT, -- For email
-- Variables
required_variables JSONB, -- Array of required variable names
sample_variables JSONB, -- Sample data for preview
-- Configuration
language VARCHAR(10) DEFAULT 'es', -- es, en, ca
is_active BOOLEAN DEFAULT TRUE,
is_system_template BOOLEAN DEFAULT FALSE, -- Cannot be modified by users
version INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(tenant_id, template_code, channel)
);
notification_preferences
CREATE TABLE notification_preferences (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
user_id UUID NOT NULL,
-- Channel preferences
email_enabled BOOLEAN DEFAULT TRUE,
whatsapp_enabled BOOLEAN DEFAULT TRUE,
sms_enabled BOOLEAN DEFAULT TRUE,
preferred_channel VARCHAR(50) DEFAULT 'email',
-- Notification type preferences
critical_alerts_enabled BOOLEAN DEFAULT TRUE,
high_priority_enabled BOOLEAN DEFAULT TRUE,
medium_priority_enabled BOOLEAN DEFAULT TRUE,
low_priority_enabled BOOLEAN DEFAULT TRUE,
-- Timing preferences
quiet_hours_start TIME, -- e.g., 22:00
quiet_hours_end TIME, -- e.g., 08:00
weekend_notifications BOOLEAN DEFAULT TRUE,
-- Specific notification types
stockout_alerts BOOLEAN DEFAULT TRUE,
quality_alerts BOOLEAN DEFAULT TRUE,
forecast_updates BOOLEAN DEFAULT TRUE,
daily_summary BOOLEAN DEFAULT TRUE,
weekly_report BOOLEAN DEFAULT TRUE,
-- Contact details
email_address VARCHAR(255),
phone_number VARCHAR(50),
whatsapp_number VARCHAR(50),
whatsapp_opt_in BOOLEAN DEFAULT FALSE,
whatsapp_opt_in_date TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(tenant_id, user_id)
);
notification_delivery_log
CREATE TABLE notification_delivery_log (
id UUID PRIMARY KEY,
notification_id UUID REFERENCES notifications(id) ON DELETE CASCADE,
attempt_number INTEGER NOT NULL,
channel VARCHAR(50) NOT NULL,
status VARCHAR(50) NOT NULL, -- success, failed, bounced
status_code VARCHAR(50),
status_message TEXT,
provider_response JSONB,
attempted_at TIMESTAMP DEFAULT NOW(),
INDEX idx_delivery_log_notification (notification_id)
);
notification_events
CREATE TABLE notification_events (
id UUID PRIMARY KEY,
notification_id UUID REFERENCES notifications(id) ON DELETE CASCADE,
event_type VARCHAR(50) NOT NULL, -- opened, clicked, bounced, complained
event_data JSONB,
user_agent TEXT,
ip_address INET,
occurred_at TIMESTAMP DEFAULT NOW(),
INDEX idx_events_notification (notification_id),
INDEX idx_events_type (notification_id, event_type)
);
notification_costs
CREATE TABLE notification_costs (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
month DATE NOT NULL, -- First day of month
channel VARCHAR(50) NOT NULL,
-- Volume
notifications_sent INTEGER DEFAULT 0,
notifications_delivered INTEGER DEFAULT 0,
-- Costs (in euros)
estimated_cost DECIMAL(10, 4) DEFAULT 0.0000,
calculated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(tenant_id, month, channel)
);
Indexes for Performance
CREATE INDEX idx_notifications_tenant_priority ON notifications(tenant_id, priority, status);
CREATE INDEX idx_notifications_sent_at ON notifications(sent_at DESC);
CREATE INDEX idx_templates_tenant_active ON notification_templates(tenant_id, is_active);
CREATE INDEX idx_preferences_user ON notification_preferences(user_id);
CREATE INDEX idx_delivery_log_status ON notification_delivery_log(status, attempted_at DESC);
Business Logic Examples
Send Email Notification
async def send_email_notification(
tenant_id: UUID,
recipient_email: str,
recipient_name: str,
subject: str,
body: str,
html_body: str = None,
priority: str = 'medium',
attachments: list = None
) -> Notification:
"""
Send email notification via SMTP.
"""
# Create notification record
notification = Notification(
tenant_id=tenant_id,
notification_type='email',
priority=priority,
channel='email',
status='queued',
recipient_name=recipient_name,
recipient_email=recipient_email,
subject=subject,
message_body=body,
attachments=attachments
)
db.add(notification)
await db.flush()
try:
# Configure SMTP
smtp_config = await get_smtp_config(tenant_id)
# Create email message
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
msg = MIMEMultipart('alternative')
msg['From'] = smtp_config.from_address
msg['To'] = recipient_email
msg['Subject'] = subject
# Add plain text body
part1 = MIMEText(body, 'plain', 'utf-8')
msg.attach(part1)
# Add HTML body if provided
if html_body:
part2 = MIMEText(html_body, 'html', 'utf-8')
msg.attach(part2)
# Add attachments
if attachments:
for attachment in attachments:
part = MIMEBase('application', 'octet-stream')
with open(attachment['path'], 'rb') as file:
part.set_payload(file.read())
encoders.encode_base64(part)
part.add_header(
'Content-Disposition',
f'attachment; filename= {attachment["filename"]}'
)
msg.attach(part)
# Send email
import smtplib
with smtplib.SMTP(smtp_config.host, smtp_config.port) as server:
if smtp_config.use_tls:
server.starttls()
if smtp_config.username and smtp_config.password:
server.login(smtp_config.username, smtp_config.password)
server.send_message(msg)
# Update notification status
notification.status = 'sent'
notification.sent_at = datetime.utcnow()
# Log delivery
log = NotificationDeliveryLog(
notification_id=notification.id,
attempt_number=1,
channel='email',
status='success'
)
db.add(log)
await db.commit()
logger.info("Email sent successfully",
notification_id=str(notification.id),
recipient=recipient_email)
return notification
except Exception as e:
notification.status = 'failed'
notification.failed_at = datetime.utcnow()
notification.failure_reason = str(e)
notification.retry_count += 1
# Log failure
log = NotificationDeliveryLog(
notification_id=notification.id,
attempt_number=notification.retry_count,
channel='email',
status='failed',
status_message=str(e)
)
db.add(log)
await db.commit()
logger.error("Email send failed",
notification_id=str(notification.id),
error=str(e))
# Retry if within limits
if notification.retry_count < notification.max_retries:
await schedule_retry(notification.id, delay_minutes=5)
raise
Send WhatsApp Notification
async def send_whatsapp_notification(
tenant_id: UUID,
recipient_phone: str,
recipient_name: str,
message: str,
priority: str = 'high',
template_name: str = None,
template_variables: dict = None
) -> Notification:
"""
Send WhatsApp notification via Twilio.
"""
# Check user opt-in
preferences = await get_user_preferences_by_phone(tenant_id, recipient_phone)
if not preferences or not preferences.whatsapp_opt_in:
raise ValueError("User has not opted in to WhatsApp notifications")
# Create notification record
notification = Notification(
tenant_id=tenant_id,
notification_type='alert',
priority=priority,
channel='whatsapp',
status='queued',
recipient_name=recipient_name,
recipient_phone=recipient_phone,
message_body=message
)
db.add(notification)
await db.flush()
try:
# Configure Twilio
from twilio.rest import Client
twilio_config = await get_twilio_config(tenant_id)
client = Client(twilio_config.account_sid, twilio_config.auth_token)
# Format phone number (E.164 format)
formatted_phone = format_phone_e164(recipient_phone)
# Send WhatsApp message
if template_name:
# Use WhatsApp template (pre-approved)
twilio_message = client.messages.create(
from_=f'whatsapp:{twilio_config.whatsapp_number}',
to=f'whatsapp:{formatted_phone}',
content_sid=template_name,
content_variables=json.dumps(template_variables) if template_variables else None
)
else:
# Send freeform message
twilio_message = client.messages.create(
from_=f'whatsapp:{twilio_config.whatsapp_number}',
to=f'whatsapp:{formatted_phone}',
body=message
)
# Update notification status
notification.status = 'sent'
notification.sent_at = datetime.utcnow()
notification.external_message_id = twilio_message.sid
notification.external_status = twilio_message.status
# Log delivery
log = NotificationDeliveryLog(
notification_id=notification.id,
attempt_number=1,
channel='whatsapp',
status='success',
status_code=twilio_message.status,
provider_response={'sid': twilio_message.sid}
)
db.add(log)
await db.commit()
logger.info("WhatsApp sent successfully",
notification_id=str(notification.id),
recipient=recipient_phone,
twilio_sid=twilio_message.sid)
return notification
except Exception as e:
notification.status = 'failed'
notification.failed_at = datetime.utcnow()
notification.failure_reason = str(e)
notification.retry_count += 1
log = NotificationDeliveryLog(
notification_id=notification.id,
attempt_number=notification.retry_count,
channel='whatsapp',
status='failed',
status_message=str(e)
)
db.add(log)
await db.commit()
logger.error("WhatsApp send failed",
notification_id=str(notification.id),
error=str(e))
# Fallback to SMS if critical
if priority == 'critical' and notification.retry_count >= notification.max_retries:
await send_sms_notification(
tenant_id, recipient_phone, recipient_name, message, priority
)
raise
def format_phone_e164(phone: str) -> str:
"""
Format phone number to E.164 standard (e.g., +34612345678).
"""
import phonenumbers
# Parse phone number (assume Spain +34 if no country code)
try:
parsed = phonenumbers.parse(phone, 'ES')
return phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
except:
# If parsing fails, return as-is
return phone
Template Rendering
async def render_notification_template(
template_id: UUID,
variables: dict
) -> dict:
"""
Render notification template with variables.
"""
# Get template
template = await db.get(NotificationTemplate, template_id)
if not template:
raise ValueError("Template not found")
# Validate required variables
required_vars = template.required_variables or []
missing_vars = [v for v in required_vars if v not in variables]
if missing_vars:
raise ValueError(f"Missing required variables: {', '.join(missing_vars)}")
# Render subject
from jinja2 import Template
subject = None
if template.subject_template:
subject_template = Template(template.subject_template)
subject = subject_template.render(**variables)
# Render body
body_template = Template(template.body_template)
body = body_template.render(**variables)
# Render HTML body if available
html_body = None
if template.html_body_template:
html_template = Template(template.html_body_template)
html_body = html_template.render(**variables)
return {
'subject': subject,
'body': body,
'html_body': html_body,
'template_name': template.template_name,
'channel': template.channel
}
Smart Channel Selection
async def send_smart_notification(
tenant_id: UUID,
user_id: UUID,
notification_type: str,
priority: str,
subject: str,
message: str,
template_id: UUID = None,
template_variables: dict = None
) -> Notification:
"""
Send notification via optimal channel based on priority and user preferences.
"""
# Get user preferences
preferences = await get_user_preferences(tenant_id, user_id)
user = await get_user(user_id)
# Determine channel based on priority and preferences
channel = None
if priority == 'critical':
# Critical: WhatsApp if enabled, else SMS, else email
if preferences.whatsapp_enabled and preferences.whatsapp_opt_in:
channel = 'whatsapp'
elif preferences.sms_enabled:
channel = 'sms'
else:
channel = 'email'
elif priority == 'high':
# High: Preferred channel if enabled
if preferences.preferred_channel == 'whatsapp' and preferences.whatsapp_enabled:
channel = 'whatsapp'
elif preferences.preferred_channel == 'sms' and preferences.sms_enabled:
channel = 'sms'
else:
channel = 'email'
else:
# Medium/Low: Email default
channel = 'email'
# Check quiet hours
if not priority == 'critical':
if await is_quiet_hours(preferences):
# Delay notification until quiet hours end
send_at = await calculate_quiet_hours_end(preferences)
logger.info("Delaying notification due to quiet hours",
user_id=str(user_id),
send_at=send_at)
return await schedule_notification(
tenant_id, user_id, channel, subject, message,
scheduled_at=send_at
)
# Send via selected channel
if channel == 'whatsapp':
return await send_whatsapp_notification(
tenant_id,
preferences.whatsapp_number or user.phone,
user.name,
message,
priority,
template_variables=template_variables
)
elif channel == 'sms':
return await send_sms_notification(
tenant_id,
user.phone,
user.name,
message,
priority
)
else: # email
# Render template if provided
if template_id:
rendered = await render_notification_template(template_id, template_variables)
subject = rendered['subject']
message = rendered['body']
html_body = rendered['html_body']
else:
html_body = None
return await send_email_notification(
tenant_id,
preferences.email_address or user.email,
user.name,
subject,
message,
html_body=html_body,
priority=priority
)
Events & Messaging
Published Events (RabbitMQ)
Exchange: notifications
Routing Keys: notifications.sent, notifications.failed, notifications.delivered
Notification Sent Event
{
"event_type": "notification_sent",
"tenant_id": "uuid",
"notification_id": "uuid",
"channel": "whatsapp",
"priority": "critical",
"recipient_name": "Juan García",
"notification_type": "stockout_alert",
"timestamp": "2025-11-06T09:00:00Z"
}
Notification Failed Event
{
"event_type": "notification_failed",
"tenant_id": "uuid",
"notification_id": "uuid",
"channel": "email",
"priority": "high",
"failure_reason": "SMTP connection timeout",
"retry_count": 2,
"max_retries": 3,
"will_retry": true,
"timestamp": "2025-11-06T10:30:00Z"
}
Notification Delivered Event
{
"event_type": "notification_delivered",
"tenant_id": "uuid",
"notification_id": "uuid",
"channel": "whatsapp",
"delivered_at": "2025-11-06T09:01:00Z",
"read_at": "2025-11-06T09:02:00Z",
"delivery_time_seconds": 60,
"timestamp": "2025-11-06T09:02:00Z"
}
Consumed Events
- From Alert Processor: Alert events trigger notifications
- From Orchestrator: Daily summaries, scheduled reports
- From Orders: Order confirmations, delivery updates
- From Production: Quality issue alerts, batch completion
- From Procurement: Stockout warnings, purchase order confirmations
Custom Metrics (Prometheus)
# Notification metrics
notifications_sent_total = Counter(
'notifications_sent_total',
'Total notifications sent',
['tenant_id', 'channel', 'priority', 'status']
)
notification_delivery_time_seconds = Histogram(
'notification_delivery_time_seconds',
'Time from creation to delivery',
['tenant_id', 'channel'],
buckets=[1, 5, 10, 30, 60, 300, 600]
)
notification_delivery_rate = Gauge(
'notification_delivery_rate_percentage',
'Notification delivery success rate',
['tenant_id', 'channel']
)
notification_costs_euros = Counter(
'notification_costs_euros_total',
'Total notification costs',
['tenant_id', 'channel']
)
email_open_rate = Gauge(
'email_open_rate_percentage',
'Email open rate',
['tenant_id', 'template_type']
)
Configuration
Environment Variables
Service Configuration:
PORT- Service port (default: 8015)DATABASE_URL- PostgreSQL connection stringREDIS_URL- Redis connection stringRABBITMQ_URL- RabbitMQ connection string
Email (SMTP) Configuration:
SMTP_HOST- SMTP server hostSMTP_PORT- SMTP server port (default: 587)SMTP_USERNAME- SMTP usernameSMTP_PASSWORD- SMTP passwordSMTP_FROM_ADDRESS- From email addressSMTP_USE_TLS- Enable TLS (default: true)
Twilio Configuration:
TWILIO_ACCOUNT_SID- Twilio account SIDTWILIO_AUTH_TOKEN- Twilio auth tokenTWILIO_WHATSAPP_NUMBER- WhatsApp sender number (format: +1234567890)TWILIO_SMS_NUMBER- SMS sender number
Delivery Configuration:
MAX_RETRY_ATTEMPTS- Maximum retry attempts (default: 3)RETRY_DELAY_MINUTES- Delay between retries (default: 5)ENABLE_QUIET_HOURS- Respect user quiet hours (default: true)BATCH_SIZE- Bulk sending batch size (default: 100)
Cost Configuration:
WHATSAPP_COST_PER_MESSAGE- Cost per WhatsApp (default: 0.005 EUR)SMS_COST_PER_MESSAGE- Cost per SMS (default: 0.08 EUR)EMAIL_COST_PER_MESSAGE- Cost per email (default: 0.001 EUR)
Development Setup
Prerequisites
- Python 3.11+
- PostgreSQL 17
- Redis 7.4
- RabbitMQ 4.1
- SMTP server or SendGrid account
- Twilio account (for WhatsApp/SMS)
Local Development
cd services/notification
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
export DATABASE_URL=postgresql://user:pass@localhost:5432/notification
export REDIS_URL=redis://localhost:6379/0
export RABBITMQ_URL=amqp://guest:guest@localhost:5672/
export SMTP_HOST=smtp.sendgrid.net
export SMTP_USERNAME=apikey
export SMTP_PASSWORD=your_sendgrid_api_key
export TWILIO_ACCOUNT_SID=your_twilio_sid
export TWILIO_AUTH_TOKEN=your_twilio_token
alembic upgrade head
python main.py
Integration Points
Dependencies
- SMTP Server - Email delivery (SendGrid, SES, SMTP)
- Twilio API - WhatsApp and SMS delivery
- Auth Service - User information
- PostgreSQL - Notification history
- Redis - Template caching
- RabbitMQ - Alert consumption
Dependents
- Alert Processor - Sends alerts via notifications
- Orders Service - Customer order notifications
- Orchestrator - Daily summaries and reports
- All Services - Critical alerts routing
- Frontend Dashboard - Notification preferences UI
Business Value for VUE Madrid
Problem Statement
Spanish bakeries struggle with:
- Delayed awareness of critical issues (stockouts discovered too late)
- Manual phone calls and texts consuming staff time
- No systematic customer communication (order confirmations)
- Expensive SMS costs (€0.08/message in Spain)
- No record of communications sent
- Staff missing important alerts
Solution
Bakery-IA Notification Service provides:
- Real-Time Alerts: WhatsApp notifications within seconds
- Multi-Channel: Email for reports, WhatsApp for urgent
- Cost Effective: WhatsApp 90% cheaper than SMS
- Customer Communication: Professional order updates
- Communication History: Complete audit trail
- Smart Routing: Right message, right channel, right time
Quantifiable Impact
Cost Savings:
- €50-150/month using WhatsApp vs. SMS (90% cost reduction)
- 3-5 hours/week saved on manual phone calls/texts (€180-300/month)
- Total: €230-450/month savings
Operational Efficiency:
- 90% faster response to critical issues (minutes vs. hours)
- 50-70% faster issue resolution with immediate awareness
- 99%+ alert delivery reliability
- 24/7 notification delivery (no manual intervention)
Customer Satisfaction:
- 20-30% improvement with order confirmation/updates
- Professional brand image with branded emails
- Customer choice of email or WhatsApp
- Personalized communication
Target Market Fit (Spanish Bakeries)
- WhatsApp Culture: Spain has 91% WhatsApp penetration rate
- Mobile First: Bakery owners/managers always on mobile
- Cost Sensitive: SMS costs high in Spain (€0.08 vs. €0.005 WhatsApp)
- Communication Style: Spanish business culture values personal touch
- GDPR Compliance: Opt-in management meets EU regulations
ROI Calculation
Investment: €0 additional (included in subscription) + Twilio costs Cost Savings: €230-450/month (vs. SMS + manual communication) Operational Value: 50-70% faster issue resolution Monthly Value: €230-450 savings + operational efficiency Annual ROI: €2,760-5,400 value per bakery Payback: Immediate (cost savings from day one)
Competitive Advantage
- WhatsApp Integration: Few Spanish bakery platforms offer WhatsApp
- Multi-Channel: Flexibility competitors don't provide
- Smart Routing: Auto-select channel by urgency/preference
- Cost Effective: 90% cheaper than SMS-only solutions
- GDPR Compliant: Built-in opt-in management
Copyright © 2025 Bakery-IA. All rights reserved.