Notification System — Implementation Plan
Gap analysis and phased implementation plan for completing the Tellus EHS notification system.
Table of Contents
- Current State Summary
- Gap Analysis
- Phase 1: Unify & Fix Foundations — COMPLETED
- Phase 2: Email Delivery — COMPLETED
- Phase 3: Scheduled Notifications
- Phase 4: General Notification Preferences
- Phase 5: Webhook Delivery (Pro Tier)
- Phase 6: Future Module Notifications
- File Change Summary
Current State Summary
Updated: 2026-02-24 after Phase 1 and Phase 2 completion.
What Works
| Feature | Status | Notes |
|---|---|---|
| Unified in-app notifications (32 types) | Working | Single notifications table with 7 categories |
| General notification types (25) | Working | DB + SSE + REST API |
| Training notification types (7) | Working | Merged from SafePath in Phase 1 |
| SSE real-time push | Working | Training notifications flow through general system |
| NotificationBell frontend | Working | 7 category icons, polling + SSE |
| Category filtering | Working | ?category=training on /api/v1/notifications |
| SafePath scheduler | Working | Uses general NotificationService + UnitOfWork |
| Background notification creation | Working | handle_notification_send() SQS handler |
| Auto-assignment notifications | Working | Background handlers create training_assigned notifications |
| Notification deduplication | Working | NotificationRepository.has_recent_notification() |
| 90-day auto-expiry | Working | expires_at + delete_expired() |
| Email delivery (15 types) | Working | Via Mailgun through background service (SQS + direct) |
| Email opt-out | Working | email_notifications_enabled boolean on core_data_users |
| Email preference API | Working | GET/PUT /api/v1/notifications/email-preference |
What Doesn't Work / Is Missing
| Gap | Impact | Priority |
|---|---|---|
| Scheduler not running on cron | Overdue/reminder/cert checks never actually execute | HIGH |
| No per-category notification preferences | Users can only toggle ALL emails, not per-category | MEDIUM |
| No webhook delivery | Pro tier feature not implemented | LOW |
| Missing notification types | sds_expiring, compliance_*, quantity_threshold not triggered | LOW |
| No notification center page | Users only see notifications in dropdown bell | LOW |
| Background notifications miss SSE | Async notifications only appear on next poll | MEDIUM |
Gap Analysis
Gap 1: Two Separate Notification Systems — RESOLVED
Resolved in Phase 1 (2026-02-24). The
safepath_notificationsandsafepath_notification_preferencestables were dropped. All training notifications now flow through the unifiednotificationstable withcategory='training'.
Gap 2: No Email Delivery — RESOLVED
Resolved in Phase 2 (2026-02-24). Email delivery implemented for 15 email-worthy notification types via Mailgun through the background service. Two dispatch paths: (1) Main service -> SQS
EMAIL_SEND-> background handler for API-originated notifications, (2) Background handlers send directly via helper function for background-originated notifications.
Gap 3: SafePath SSE Gap — RESOLVED
Resolved in Phase 1 (2026-02-24). Training notifications now use the general
NotificationServicewhich integrates with the SSE push system.
Gap 4: Scheduler Not Running — OPEN
Problem: SafePathScheduler has methods for overdue checks, reminders, and cert expiry, but there's no cron job or background task that actually calls run_all_scheduled_tasks().
Impact: Overdue assignments are never flagged, reminders are never sent, cert expiry alerts are never generated.
Solution: Add a scheduled task in the background service. See Phase 3.
Gap 5: Auto-Assignment Notification Gap — RESOLVED
Resolved in Phase 1 (2026-02-24). Both
handle_safepath_rule_evaluate()andhandle_safepath_plan_retraining()now INSERT atraining_assignednotification into the generalnotificationstable after creating each assignment.
Phase 1: Unify & Fix Foundations — COMPLETED
Completed: 2026-02-24. All steps implemented and migration applied to dev database.
Goal: Merge SafePath notifications into the general system, fix auto-assignment notifications, remove separate SafePath notification tables.
What Was Done
| Step | File | Action | Description |
|---|---|---|---|
| 1 | service/alembic/versions/...-dr0pn0t1f_drop_safepath_notification_tables.py | CREATE | Drop safepath_notifications + safepath_notification_preferences tables |
| 2 | service/app/db/models/safepath.py | EDIT | Removed 2 notification model classes |
| 3 | service/app/db/models/notification.py | EDIT | Added 7 training types + category map |
| 4 | service/app/services/safepath/notification_service.py | DELETE | Removed SafePath-specific NotificationService |
| 5 | service/app/services/notification_service.py | EDIT | Added training templates + category filter |
| 6 | service/app/db/repositories/notification_repository.py | EDIT | Added category filter to queries |
| 7 | service/app/api/v1/notifications.py | EDIT | Added category query parameter |
| 8 | service/app/services/safepath/scheduler.py | EDIT | Uses general NotificationService + UnitOfWork |
| 9 | service/app/api/v1/safepath/automation.py | EDIT | Refactored notification endpoints, removed preferences |
| 10 | service/app/schemas/safepath/automation.py | EDIT | Removed preference schemas |
| 11 | bg-service/app/services/sqs_consumer/handlers.py | EDIT | Added notification INSERTs after auto-assignment |
| 12 | ui/src/pages/safepath/automation/index.tsx | EDIT | Uses general notification API |
| 13 | ui/src/services/api/notification.api.ts | EDIT | Added category parameter |
| 14 | ui/src/services/api/safepath.api.ts | EDIT | Removed 5 notification/preference functions |
| 15 | ui/src/pages/safepath/automation/notifications.tsx | DELETE | Removed preferences page |
| 15b | ui/src/App.tsx | EDIT | Removed notification settings route |
| 16 | ui/src/types/safepath.ts | EDIT | Removed 4 notification interfaces |
Total: 3 new/deleted files, 13 modified files
Phase 2: Email Delivery — COMPLETED
Completed: 2026-02-24. All 10 steps implemented. Migration applied to dev database. TypeScript clean.
Goal: Implement email notification delivery for critical notification types via Mailgun through the background service (async via SQS).
Architecture
Main Service (API) SQS Queue Background Service
+--------------------+ +--------------------+
| NotificationSvc | | |
| .notify_user() |--creates in-app notification-->DB | |
| | | |
| if email-worthy: |--dispatch_email()-->[EMAIL_SEND]--> | handle_email_send |
| | | -> check opt-out |
| | | -> render HTML |
| | | -> Mailgun API |
+--------------------+ +--------------------+
Background handlers also send emails directly after creating notifications
(no re-queue through SQS).
What Was Done
| Step | File | Action | Description |
|---|---|---|---|
| 1 | service/alembic/versions/...-em41ln0t1f_add_email_notifications_enabled.py | CREATE | Add email_notifications_enabled BOOLEAN DEFAULT TRUE to core_data_users |
| 1b | service/app/db/models/user.py | EDIT | Add email_notifications_enabled column to User model |
| 2 | service/app/db/models/notification.py | EDIT | Add EMAIL_WORTHY_TYPES constant (15 types) |
| 3 | service/app/services/notification_service.py | EDIT | Wire email dispatch into notify_user() and _create_for_recipients() |
| 4 | bg-service/app/core/config.py | EDIT | Add Mailgun config + FRONTEND_URL |
| 5 | bg-service/app/services/email/__init__.py | CREATE | Package init, exports mailgun_client |
| 5b | bg-service/app/services/email/mailgun_client.py | CREATE | Async Mailgun HTTP client using httpx |
| 6 | bg-service/app/services/email/templates.py | CREATE | Subject lines + HTML/text templates with urgency styling |
| 7 | bg-service/app/services/sqs_consumer/handlers.py | EDIT | Implement handle_email_send() (replaced stub) |
| 8 | bg-service/app/services/email/send_notification_email.py | CREATE | Helper for background handlers to send emails directly |
| 8b | bg-service/app/services/sqs_consumer/handlers.py | EDIT | Add email calls to 4 handlers: handle_notification_send, _dispatch_ai_notification, handle_safepath_rule_evaluate, handle_safepath_plan_retraining |
| 9 | service/app/api/v1/notifications.py | EDIT | Add GET/PUT /email-preference endpoints |
| 10 | ui/src/services/api/notification.api.ts | EDIT | Add getEmailPreference() + updateEmailPreference() |
Total: 5 new files, 7 modified files
Email-Worthy Types (15)
EMAIL_WORTHY_TYPES = {
# Workflow - major events
"plan_approved", "plan_rejected", "plan_published",
# Inventory - bulk operations
"chemical_bulk_imported", "sds_expiring",
# Compliance - safety critical
"quantity_threshold_exceeded", "plan_review_due", "plan_overdue",
# AI - user waiting
"ai_generation_completed", "ai_generation_failed",
# Training - action required / safety
"training_assigned", "training_reminder", "training_overdue",
"cert_expiring", "cert_expired",
}
Email Template Urgency Styling
| Urgency | Banner Color | Types |
|---|---|---|
| Urgent | Red (#dc2626) | training_overdue, cert_expired, plan_overdue, ai_generation_failed, quantity_threshold_exceeded |
| Warning | Amber (#d97706) | training_reminder, cert_expiring, sds_expiring, plan_review_due, plan_rejected |
| Normal | Blue (#2563eb) | All other email-worthy types |
Phase 3: Scheduled Notifications
Goal: Wire up SafePath scheduler to actually run in the background service.
Status: OPEN — Next priority
3.1 New SQS Message Type
Add SAFEPATH_SCHEDULED_CHECK message type.
3.2 New Background Handler
Implement handle_safepath_scheduled_check() that runs overdue checks, reminders, and cert expiry checks for a company.
3.3 Cron Trigger
Option A (Recommended for production): CloudWatch Events / EventBridge rule that sends an SQS message every 6 hours.
Option B (For local dev): apscheduler inside the background service.
Phase 4: General Notification Preferences
Goal: Allow users to control per-category notification preferences with frequency options.
Status: OPEN — Detailed plan at Notification Preferences Plan
Key features:
- Per-category preferences (7 categories) with three-tier resolution (user -> company -> system)
- Three frequency options:
immediate,daily_digest,off - Always-immediate override for 5 safety-critical types
- Daily digest batching via
pending_digest_emailsstaging table - Frontend settings page at
/settings/notification-preferences
Phase 5: Webhook Delivery (Pro Tier)
Goal: Allow enterprise customers to receive notifications via webhook (Slack, Teams, custom).
Status: OPEN — Low priority
Key features:
notification_webhookstable with URL, secret, category filter- Webhook dispatcher via SQS
WEBHOOK_SENDmessage type - Admin UI for webhook configuration
- HMAC signature verification for security
Phase 6: Future Module Notifications
Goal: Add notification support for planned modules (AdminHQ, Inspections, Incidents).
Status: OPEN — Triggered when each module is implemented
Pattern for Adding New Module Notifications
- Define types in
NotificationTypeclass andNOTIFICATION_CATEGORY_MAP - Add title/message templates in
NotificationService - If email-worthy, add to
EMAIL_WORTHY_TYPESand create email subject template - Wire triggers in the module's API/service layer
- Frontend category icon handled automatically if reusing existing categories
File Change Summary
Phase 1 (Unify & Fix) — COMPLETED 2026-02-24
3 new/deleted files, 13 modified files — See Phase 1 section above.
Phase 2 (Email Delivery) — COMPLETED 2026-02-24
5 new files, 7 modified files — See Phase 2 section above.
Phase 3 (Scheduler) — TODO
| # | File | Action | Description |
|---|---|---|---|
| 1 | service/app/core/sqs_config.py | EDIT | Add SAFEPATH_SCHEDULED_CHECK |
| 2 | bg-service/app/services/sqs_consumer/handlers.py | EDIT | Add handle_safepath_scheduled_check() |
| 3 | bg-service/app/services/sqs_consumer/service.py | EDIT | Register handler |
Phase 4 (General Preferences) — TODO
See Notification Preferences Plan — 6 new files, 13 modified files
Phase 5 (Webhooks) — TODO
| # | File | Action | Description |
|---|---|---|---|
| 1 | service/alembic/versions/..._notification_webhooks.py | CREATE | notification_webhooks table |
| 2 | service/app/db/models/notification.py | EDIT | Add NotificationWebhook model |
| 3 | service/app/services/webhook_service.py | CREATE | Webhook dispatch logic |
| 4 | service/app/core/sqs_config.py | EDIT | Add WEBHOOK_SEND |
| 5 | bg-service/app/services/sqs_consumer/handlers.py | EDIT | Add handle_webhook_send() |
| 6 | service/app/api/v1/webhooks.py | CREATE | Webhook CRUD API |
| 7 | ui/src/pages/settings/webhooks.tsx | CREATE | Webhook management UI |
Priority & Dependencies
Phase 1 (Unify) <-- COMPLETED 2026-02-24
|
Phase 2 (Email) <-- COMPLETED 2026-02-24
|
Phase 3 (Scheduler) <-- NEXT (enables overdue/reminder/cert notifications)
|
Phase 4 (Preferences) <-- Per-category prefs with daily digest
|
Phase 5 (Webhooks) <-- Pro tier, lowest priority
|
Phase 6 (New Modules) <-- As modules are built