Exception-Based Triage System
The Vantrexia triage system uses an exception-based model to enable a single clinician to effectively monitor up to 100 patients simultaneously. Rather than reviewing every patient's data, the system automatically identifies patients with abnormal readings, ranks them by clinical risk, and presents a priority-ordered queue that focuses clinical attention where it matters most.
Traditional RPM workflows require clinicians to manually review each patient's readings. Vantrexia's exception-based approach surfaces only the patients who deviate from their expected parameters, making it feasible for a single clinician to oversee a panel of 100 patients with confidence.
Visual Severity Hierarchy
The triage dashboard uses a four-level color-coded severity hierarchy to provide instant visual understanding of patient status. Every triage view — from summary cards to patient lists to individual patient details — uses this consistent color system:
| Level | Color | Risk Score | Criteria | Action Required |
|---|---|---|---|---|
| Critical | Red | 76–100 | Vital signs severely outside threshold; multiple simultaneous breaches; STAT escalation active | Immediate clinician review and intervention |
| Warning | Orange | 26–75 | Vital signs moderately outside threshold; trending toward critical; repeated threshold breaches | Review within 1 hour; consider escalation |
| No Data | Gray | — | No recent device transmissions; patient may be non-compliant or experiencing device issues | Contact patient; verify device connectivity |
| Normal | Green | 0–25 | All vitals within configured thresholds; device transmitting normally | No action required; routine oversight |
Triage Summary Cards
The top of the triage dashboard displays four summary cards, one for each severity level. Each card shows the count of patients currently at that level and the trend direction (increasing, stable, or decreasing) compared to the previous 24-hour period.
GET /api/v1/triage/patients/stats/
{
"critical": { "count": 3, "trend": "stable", "change": 0 },
"warning": { "count": 18, "trend": "increasing", "change": 4 },
"no_data": { "count": 42, "trend": "decreasing", "change": -7 },
"normal": { "count": 784, "trend": "stable", "change": 2 },
"total_patients": 847,
"last_calculated": "2026-02-06T14:30:00Z"
}
Priority Patient List
Below the summary cards, the triage dashboard displays a priority-ordered list of patients sorted by risk score (highest first). Each row in the list shows:
- Risk indicator — color-coded badge matching the severity hierarchy
- Patient name and MRN — encrypted at rest, decrypted only for authorized viewers
- Risk score — numeric value from 0 to 100
- Primary alert — the most recent or most severe threshold breach
- Last reading — timestamp of the most recent vital sign observation
- Active alerts — count of unacknowledged alerts for this patient
- Days since last transmission — highlights potential non-compliance
The priority list can be filtered by severity level, assigned clinician, vital type, or date range. Use the filter bar above the list to narrow your view. Sorting defaults to risk score descending but can be switched to sort by last reading time, alert count, or patient name.
Alert Management
When a patient's vital reading breaches a configured threshold, the system creates an alert record and updates the patient's triage summary. Clinicians manage alerts through a structured acknowledge workflow:
Alert Triggered
The observation pipeline detects a threshold breach and creates an alert with severity, the triggering vital values, and the threshold that was breached. The patient's risk score is recalculated immediately.
Clinician Notified
A push notification is sent to the assigned clinician. The notification contains a sanitized message (no PHI) with a deep link to the patient's triage detail view. The triage dashboard highlights the new alert.
Review & Acknowledge
The clinician opens the patient's detail view, reviews the vital sign data, and acknowledges the alert. Acknowledgment requires selecting an outcome: Resolved, Monitoring, or Escalated. An optional clinical note can be attached.
Risk Score Recalculated
After acknowledgment, the patient's risk score is recalculated factoring in the clinician's response. If the underlying readings return to normal, the patient moves to a lower severity tier. All actions are recorded in the audit log.
curl -X POST /api/v1/alerts/{alert_id}/acknowledge/ \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"outcome": "monitoring",
"note": "Patient reports taking medication late. Will follow up in 2 hours.",
"follow_up_minutes": 120
}'
Escalation System
Alerts that require elevated clinical attention can be escalated through a three-tier priority system. Escalations create formal records that track the handoff between care team members and generate billable events.
| Priority | Response Time | Trigger Criteria | Notification |
|---|---|---|---|
| Routine | Within 24 hours | Clinician manually escalates for non-urgent follow-up; trending readings that need provider review | In-app notification to assigned provider |
| Urgent | Within 4 hours | Repeated threshold breaches within 24 hours; patient-reported worsening symptoms; MA triage identifies concern | Push notification + in-app alert to provider |
| STAT | Immediate | Critical vital signs (e.g., BP > 180/120); risk score > 90; multiple simultaneous critical breaches | Push notification + SMS + in-app alert to provider and supervising physician |
Each escalation event is automatically logged as a billable activity for CPT 99457/99458 (interactive communication with patient or caregiver). The billing engine captures the escalation time, involved staff, and priority level to support claims documentation.
Per-Patient Threshold Management
Vantrexia supports configurable alert thresholds on a per-patient basis. This allows clinicians to set individualized parameters based on the patient's condition, medications, and clinical history — rather than relying on population-wide defaults.
Default Thresholds
| Vital Sign | Low Warning | Low Critical | High Warning | High Critical | Unit |
|---|---|---|---|---|---|
| Systolic BP | 90 | 80 | 140 | 180 | mmHg |
| Diastolic BP | 60 | 50 | 90 | 120 | mmHg |
| Pulse Rate | 50 | 40 | 100 | 130 | bpm |
| SpO2 | 95 | 90 | — | — | % |
| Blood Glucose | 70 | 54 | 180 | 300 | mg/dL |
| Weight Change | — | — | 3 lbs/day | 5 lbs/day | lbs |
Every threshold modification is recorded in the HIPAA audit log with the clinician's identity, the previous value, the new value, the patient ID, and a timestamp. This creates a defensible record of clinical decision-making for compliance reviews.
curl -X PATCH /api/v1/patients/{patient_id}/vital-config/ \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"bp_systolic_min": 85,
"bp_systolic_max": 190,
"reason": "Patient on antihypertensive medication; adjusted per Dr. Smith"
}'
Non-Compliance Tracking
The triage system monitors device transmission patterns to identify patients who are not using their monitoring equipment as prescribed. Non-compliance impacts both clinical outcomes and billing eligibility (CPT 99454 requires ≥16 transmission days per 30-day period).
Non-compliance is surfaced in three ways:
- Triage list badge: A "Non-compliant" badge appears next to patients who have missed transmissions for 2+ consecutive days
- Daily digest: The system generates a daily non-compliance report listing all patients below the transmission threshold
- Risk score impact: Extended non-compliance increases the patient's risk score, as missing data may indicate worsening condition or patient disengagement
Data Models
PatientTriageSummary
The core triage model that aggregates a patient's current clinical status into a single queryable record:
class PatientTriageSummary(models.Model):
patient = models.OneToOneField(Patient, on_delete=models.CASCADE, related_name='triage_summary')
risk_level = models.CharField(
max_length=20,
choices=[
('critical', 'Critical'),
('warning', 'Warning'),
('normal', 'Normal'),
('no_data', 'No Data'),
],
default='normal',
db_index=True
)
risk_score = models.IntegerField(
default=0,
validators=[MinValueValidator(0), MaxValueValidator(100)],
help_text="Composite risk score from 0 (lowest) to 100 (highest)"
)
active_alerts_count = models.IntegerField(default=0)
last_vital_reading = models.DateTimeField(null=True, blank=True)
last_transmission = models.DateTimeField(null=True, blank=True)
days_non_compliant = models.IntegerField(default=0)
assigned_clinician = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
last_reviewed_at = models.DateTimeField(null=True, blank=True)
last_reviewed_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-risk_score', '-active_alerts_count']
EscalationEvent
Records formal escalation events in the clinical workflow:
class EscalationEvent(models.Model):
patient = models.ForeignKey(Patient, on_delete=models.CASCADE, related_name='escalations')
priority = models.CharField(
max_length=10,
choices=[('routine', 'Routine'), ('urgent', 'Urgent'), ('stat', 'STAT')]
)
reason = models.TextField(help_text="Clinical reason for escalation")
triggering_alert = models.ForeignKey(Alert, null=True, on_delete=models.SET_NULL)
escalated_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='escalations_created')
escalated_to = models.ForeignKey(User, on_delete=models.CASCADE, related_name='escalations_received')
status = models.CharField(
max_length=20,
choices=[('pending', 'Pending'), ('acknowledged', 'Acknowledged'), ('in_progress', 'In Progress'), ('resolved', 'Resolved'), ('cancelled', 'Cancelled')],
default='pending'
)
resolution_note = models.TextField(blank=True)
resolved_at = models.DateTimeField(null=True, blank=True)
resolved_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
billable = models.BooleanField(default=True)
billing_record = models.ForeignKey('billing.BillingRecord', null=True, on_delete=models.SET_NULL)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
Risk Score Algorithm
The risk score is a composite value from 0 to 100 that represents the overall clinical urgency for a patient. It is recalculated every time a new observation arrives, an alert is created or acknowledged, or a threshold is changed.
The algorithm weighs the following factors:
| Factor | Weight | Description |
|---|---|---|
| Active Critical Alerts | 40% | Each unacknowledged critical alert contributes up to 40 points |
| Active Warning Alerts | 20% | Each unacknowledged warning alert contributes up to 20 points |
| Threshold Deviation | 20% | How far the latest reading is from the threshold boundary (normalized) |
| Trend Direction | 10% | Worsening trend over last 3 readings increases score; improving trend decreases it |
| Non-Compliance | 10% | Days without transmission: 0–1 = 0 pts, 2–3 = 3 pts, 4–7 = 7 pts, 7+ = 10 pts |
def calculate_risk_score(patient):
score = 0
# Active critical alerts (up to 40 points)
critical_count = patient.alerts.filter(severity='critical', acknowledged_at__isnull=True).count()
score += min(critical_count * 20, 40)
# Active warning alerts (up to 20 points)
warning_count = patient.alerts.filter(severity='warning', acknowledged_at__isnull=True).count()
score += min(warning_count * 10, 20)
# Threshold deviation (up to 20 points)
latest = patient.observations.order_by('-effective_datetime').first()
if latest and latest.threshold_deviation:
score += min(int(latest.threshold_deviation * 20), 20)
# Trend direction (up to 10 points)
trend = calculate_vital_trend(patient, readings=3)
if trend == 'worsening':
score += 10
elif trend == 'stable':
score += 3
# Non-compliance penalty (up to 10 points)
days_silent = (timezone.now() - patient.last_transmission).days if patient.last_transmission else 30
if days_silent >= 7:
score += 10
elif days_silent >= 4:
score += 7
elif days_silent >= 2:
score += 3
return min(score, 100)