Notifications & Alerts
Vantrexia delivers real-time notifications to clinicians and staff through push notifications (Firebase Cloud Messaging), in-app alerts, and scheduled digests. Every notification payload is HIPAA-compliant — protected health information is automatically sanitized before any data leaves the server.
Push notifications are delivered through third-party infrastructure (Google FCM / Apple APNs) and may be visible on device lock screens. No PHI is ever included in push notification payloads. Patient names, MRNs, dates of birth, and specific vital sign values are stripped from all push content. Detailed information is only accessible after authentication within the app.
Push Notification Architecture
Vantrexia uses Firebase Cloud Messaging (FCM) as the push notification delivery service for both iOS and Android devices. The architecture supports two token types depending on the deployment environment:
| Environment | Token Type | Service | Use Case |
|---|---|---|---|
| Development | Expo Push Token | Expo Push Service | Testing with Expo Go app during local development |
| Production | FCM Device Token | Firebase Cloud Messaging | Production builds deployed to App Store / Play Store |
The mobile app automatically detects whether it's running in Expo Go (development) or as a standalone build (production) and registers the appropriate token type. The backend accepts both token formats and routes the notification through the correct delivery service.
Delivery Flow
Event Triggered
A platform event occurs that requires notification — a vital alert, escalation, billing event, or scheduled reminder. The event is created in the database with full context including the triggering data.
PHI Sanitized
The notification builder extracts a safe notification message from the event. The PHI sanitizer strips all protected health information — patient names are replaced with "a patient", MRNs are removed, and specific vital values are generalized (e.g., "elevated blood pressure" instead of "BP 182/110").
Recipient Resolved
The system determines the notification recipients based on the event type and the patient's assigned care team. For escalations, both the assigned clinician and the escalation target receive notifications.
Push Delivered
The sanitized notification payload is sent to FCM (production) or Expo Push Service (development). FCM handles delivery to the device, including retry logic for offline devices. A delivery record is created in the database.
In-App Record
Simultaneously, an in-app notification record is created with the full event context (including PHI). This record is accessible only after the user authenticates within the app and is encrypted at rest.
PHI Sanitization
The PHI sanitization engine processes every outbound notification message through a series of regex-based filters and replacement rules. This ensures that even if a developer accidentally includes PHI in a notification template, it will be stripped before transmission.
Sanitization Rules
| Pattern | Matches | Replacement |
|---|---|---|
| MRN patterns | MRN-\d{4}-\d{6}, MRN:\s*\w+ |
Removed entirely |
| SSN patterns | \d{3}-\d{2}-\d{4} |
Removed entirely |
| DOB patterns | \d{4}-\d{2}-\d{2} in context of "born", "DOB", "date of birth" |
Removed entirely |
| Patient names | Any string matching a known patient name from the event context | "a patient" or "your patient" |
| Specific vital values | Numeric values adjacent to vital sign units (mmHg, bpm, mg/dL, etc.) | Generalized descriptions ("elevated", "low", "abnormal") |
| Phone numbers | \+?1?\d{10}, \(\d{3}\)\s*\d{3}-\d{4} |
Removed entirely |
import re
class PHISanitizer:
"""
Strips protected health information from notification messages
before they are sent through external push services.
"""
PATTERNS = [
# MRN patterns
(r'MRN[-:]?\s*[\w-]+', ''),
# SSN patterns
(r'\b\d{3}-\d{2}-\d{4}\b', ''),
# Phone numbers
(r'\+?1?\s*\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}', ''),
# Specific BP values (e.g., "182/110 mmHg")
(r'\b\d{2,3}/\d{2,3}\s*mmHg\b', 'abnormal blood pressure'),
# Heart rate values (e.g., "142 bpm")
(r'\b\d{2,3}\s*bpm\b', 'abnormal heart rate'),
# SpO2 values (e.g., "88%")
(r'\b\d{2,3}%\s*(?:SpO2|oxygen)', 'low oxygen saturation'),
# Blood glucose (e.g., "320 mg/dL")
(r'\b\d{2,3}\s*mg/dL\b', 'abnormal glucose level'),
# Temperature (e.g., "103.2°F")
(r'\b\d{2,3}\.\d°?[FC]\b', 'abnormal temperature'),
]
@classmethod
def sanitize(cls, message: str, patient_names: list[str] = None) -> str:
"""Remove all PHI patterns from a notification message."""
sanitized = message
# Replace patient names
if patient_names:
for name in patient_names:
sanitized = sanitized.replace(name, 'a patient')
# Apply regex patterns
for pattern, replacement in cls.PATTERNS:
sanitized = re.sub(pattern, replacement, sanitized, flags=re.IGNORECASE)
# Clean up double spaces and trailing punctuation artifacts
sanitized = re.sub(r'\s{2,}', ' ', sanitized).strip()
return sanitized
Use the management command python manage.py test_phi_sanitizer to run the sanitizer against a set of test messages and verify that all PHI patterns are correctly removed. The test suite includes edge cases for partial matches, concatenated identifiers, and locale-specific date formats.
Notification Channels
Notifications are organized into four channels that determine delivery behavior, priority, and user opt-in/opt-out controls:
| Channel | Priority | Delivery | Default | Description |
|---|---|---|---|---|
| Escalations | High | Immediate push + in-app | Always on | STAT and Urgent escalation events. Cannot be disabled by users — these are critical clinical notifications that require immediate attention. |
| Alerts | Medium | Immediate push + in-app | On | Vital sign threshold breaches, non-compliance warnings, and system health changes. Users can opt out of non-critical subtypes. |
| Reminders | Normal | Push + in-app | On | Follow-up reminders, pending review notifications, billing task reminders. Delivered at scheduled times to avoid interrupting clinical workflows. |
| Daily Digest | Low | Push (once daily) + in-app | On | Morning summary at 7:00 AM local time with overnight alerts, non-compliance counts, and billing pipeline status. Can be customized or disabled by users. |
curl -X PATCH /api/v1/users/me/notification-preferences/ \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"channels": {
"escalations": { "push": true, "in_app": true },
"alerts": { "push": true, "in_app": true },
"reminders": { "push": false, "in_app": true },
"daily_digest": { "push": true, "in_app": true, "time": "07:00" }
},
"quiet_hours": {
"enabled": true,
"start": "22:00",
"end": "06:00",
"exceptions": ["escalations"]
}
}'
When quiet hours are enabled, all push notifications except those on the exceptions list are silenced during the specified window. In-app notifications are still created and will be visible when the user opens the app. Escalation channel notifications always bypass quiet hours because they represent critical clinical situations.
Token Management
Push notification tokens are managed through a registration endpoint that the mobile app calls during login and periodically to keep tokens fresh.
curl -X POST /api/v1/notifications/tokens/register/ \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"token": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
"type": "expo",
"device_id": "device-uuid-abc-123",
"platform": "ios",
"app_version": "1.2.0"
}'
The token management system handles the following scenarios:
- Token refresh: When a device token changes (common with FCM), the app re-registers and the old token is automatically deactivated.
- Multi-device support: A user can have active tokens on multiple devices. Notifications are delivered to all active tokens.
- Stale token cleanup: A Celery task runs weekly to test all registered tokens. Tokens that FCM reports as invalid are automatically removed from the database.
- Logout cleanup: When a user logs out, all tokens associated with that device are deactivated to prevent notifications being delivered to a shared device after logout.
Alert Thresholds
Vital sign alerts are triggered when a patient's device reading breaches their configured thresholds. Thresholds are configurable per-patient (see Triage System — Per-Patient Thresholds) and default to clinically accepted ranges if no custom values are set.
The alert generation pipeline operates as follows:
def process_observation(observation):
"""
Called for every new vital sign observation.
Checks against patient thresholds and generates alerts.
"""
patient = observation.patient
thresholds = patient.get_thresholds(observation.vital_type)
if not thresholds:
return # No thresholds configured for this vital type
value = observation.value_quantity
# Determine severity based on threshold breach
severity = None
if value >= thresholds.high_critical or value <= thresholds.low_critical:
severity = 'critical'
elif value >= thresholds.high_warning or value <= thresholds.low_warning:
severity = 'warning'
if severity:
# Create alert record
alert = Alert.objects.create(
patient=patient,
observation=observation,
vital_type=observation.vital_type,
severity=severity,
value=value,
threshold_breached=thresholds.get_breached_threshold(value),
message=f"{observation.get_vital_display()} reading of {value} "
f"exceeds {severity} threshold"
)
# Trigger notification
send_vital_alert_notification.delay(
alert_id=alert.id,
recipient_ids=patient.get_care_team_ids()
)
# Update triage summary
recalculate_risk_score.delay(patient_id=patient.id)
Acknowledge Workflow
Every vital alert requires acknowledgment by a clinician. The acknowledge workflow ensures that no alert goes unaddressed and creates a documented record of clinical response for compliance purposes.
| Alert State | Description | Actions Available |
|---|---|---|
| Active | Alert has been created but not yet reviewed by a clinician | View details, Acknowledge, Escalate |
| Acknowledged | A clinician has reviewed the alert and documented their response | Add notes, Escalate, Resolve |
| Resolved | The clinical situation has been addressed and closed | View audit trail (read-only) |
| Auto-Resolved | Subsequent readings returned to normal range; auto-resolved after 24 hours | View audit trail (read-only) |
Alerts are automatically resolved if the patient's subsequent readings (within 24 hours) return to within normal thresholds and no clinician has manually acknowledged the alert. Auto-resolved alerts are flagged separately from clinician-resolved alerts in the audit log to maintain distinct documentation.
curl -X POST /api/v1/alerts/{alert_id}/acknowledge/ \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"outcome": "resolved",
"clinical_note": "Patient contacted by phone. Reports taking medication as prescribed. BP reading likely due to physical activity. No intervention required.",
"follow_up_required": false
}'
# Response:
{
"id": "alert-uuid-789",
"status": "resolved",
"acknowledged_by": "Dr. Sarah Chen",
"acknowledged_at": "2026-02-06T14:45:00Z",
"outcome": "resolved",
"audit_trail": [
{
"action": "created",
"timestamp": "2026-02-06T14:28:12Z",
"actor": "system"
},
{
"action": "notification_sent",
"timestamp": "2026-02-06T14:28:15Z",
"actor": "system",
"detail": "Push notification sent to Dr. Sarah Chen"
},
{
"action": "acknowledged",
"timestamp": "2026-02-06T14:45:00Z",
"actor": "Dr. Sarah Chen",
"detail": "Resolved with clinical note"
}
]
}
Lock Screen Privacy
Push notifications are configured with lock screen privacy in mind. The mobile app sets the following platform-specific options to prevent PHI exposure on unattended devices:
| Platform | Setting | Effect |
|---|---|---|
| iOS | showPreviews: "whenAuthenticated" |
Notification content is hidden until Face ID/Touch ID/passcode is used |
| Android | visibility: "private" |
Shows a redacted notification on the lock screen; full content visible only when unlocked |
| Both | Generic push titles | Push titles use "Vantrexia Alert" rather than any patient-identifying information |
Lock screen privacy is one layer in a defense-in-depth strategy. Even if a device's lock screen settings are misconfigured, push notification payloads never contain PHI (enforced server-side by the PHI sanitizer). This means there is no PHI to expose regardless of the device's display settings.