Alert governance (fighting fatigue)
SauBit emits deterministic clinical alerts (clinical_alerts) on top of drug-drug
interactions. In real settings, alert overload causes fatigue: clinicians start ignoring
warnings — including critical ones. Alert governance lets each organization tune what is
shown without ever changing the clinical truth.
Safety principles
- Suppression is presentation, not diagnosis. A suppressed alert still counts towards
the
prescription_risk_level. Governance never lowers prescription risk. - Critical risk cannot be hidden. Alerts at/above
confirm_min_severity(defaultCONTRAINDICADO) are always surfaced and flaggedrequires_confirmation: true— no policy lever can suppress them. - Every suppression is auditable. Each suppressed item carries the reason
(
suppression_reason) and, for known-alert overrides, the recorded justification.
Response fields
Each clinical_alerts item exposes:
| Field | Description |
|---|---|
id | Stable alert id (drug-order independent). Use it to suppress/justify known alerts. |
requires_confirmation | true when explicit confirmation is required before proceeding. |
And the response gains the alert_governance block:
"alert_governance": {
"applied": true,
"policy_enabled": true,
"view": "pharmacist",
"unit": "icu",
"min_severity": "MODERADO",
"confirm_min_severity": "CONTRAINDICADO",
"requires_confirmation": true,
"confirmation_required_ids": ["drug_allergy-1a2b3c4d5e"],
"total_alerts": 4,
"visible_count": 2,
"suppressed_count": 2,
"suppressed": [
{ "type": "food_alcohol", "severity": "LEVE", "id": "food_alcohol-9f8e7d6c5b", "suppression_reason": "below_min_severity", "justification": "" }
]
}
Suppression reasons:
| Reason | Meaning |
|---|---|
below_min_severity | Below the configured minimum severity. |
suppressed_type | Alert type suppressed by policy (global or per unit). |
policy_override | Known alert suppressed with a recorded justification. |
out_of_view | Outside the role view (e.g. drug_disease in the pharmacist view). |
Role views (physician × pharmacist)
Send the X-SauBit-Alert-View header (or ?alert_view=) with medico or farmaceutico:
curl -X POST https://api.saubit.com.br/api/v1/interactions/check \
-H "X-API-Key: sm_live_..." \
-H "X-SauBit-Alert-View: farmaceutico" \
-H "Content-Type: application/json" \
-d '{ "medications": [ { "name": "ibuprofeno" } ], "patient_record": { "conditions": ["insuficiência renal"] } }'
medico— sees every alert type.farmaceutico— sees operational/dispensing alerts (allergy, duplication, max dose, IV compatibility, drug × lab, renal/hepatic adjustment); diagnostic-reasoning alerts (e.g.drug_disease) leave the list without_of_view.
Critical alerts are always surfaced, regardless of the view.
Role security: when the API key has a bound role (set by the admin —
physician/pharmacist), it is authoritative and theX-SauBit-Alert-Viewheader is ignored (it cannot escalate privilege). The header applies only as a presentation hint when the key has no bound role. Suppression is display-only and never lowers risk — but the authentic role must come from identity, not a free header.
Hospital unit
The unit is inferred from patient_context.setting (e.g. "UTI", "Pediatria",
"Oncologia") and normalized to a tag (icu, pediatrics, oncology, emergency,
obstetrics). A policy can define per-unit rules.
Policy (per organization / API key)
The policy is a JSON object configured per organization (with per-API-key override). Fields:
| Field | Type | Effect |
|---|---|---|
enabled | bool | When false, becomes pass-through (keeps only critical confirmation). |
min_severity | LEVE·MODERADO·GRAVE·CONTRAINDICADO | Suppresses alerts below the threshold. |
confirm_min_severity | same | Severity at/above which confirmation is required (default CONTRAINDICADO). |
suppressed_types | list | Alert types always suppressed (e.g. ["food_alcohol"]). |
suppressed_signatures | list | Known-alert overrides: { "signature": "<id>", "justification": "...", "expires_at"?: "ISO 8601" }. With expires_at, the override expires and the alert resurfaces. |
unit_rules | object | Per-unit override: { "UTI": { "min_severity": "GRAVE" } }. |
role_types | object | Visible types per role: { "pharmacist": ["drug_allergy", "max_dose"] }. |
Example policy:
{
"enabled": true,
"min_severity": "MODERADO",
"confirm_min_severity": "CONTRAINDICADO",
"suppressed_types": ["food_alcohol"],
"unit_rules": {
"UTI": { "min_severity": "GRAVE" },
"Pediatria": { "suppressed_types": [] }
},
"suppressed_signatures": [
{ "signature": "duplicate_therapy-1a2b3c4d5e", "justification": "Approved institutional protocol" }
]
}
Configuration (admin routes)
The policy is configured through admin routes (admin JWT, under /api/v1/admin/*), with
the API key policy taking precedence over the organization policy. As admin routes,
they are documented in the Admin Swagger (/docs/admin), kept separate from the hospital
routes — including GET/PUT/DELETE …/organizations/{org_id}/alert-governance and
…/api-keys/{key_id}/alert-governance.
Invalid input (severity outside the set, unknown alert type, override without justification)
returns 422. Values are normalized (e.g. min_severity upper-cased, unit converted to a tag).
Propagation: API-key authentication is cached for ~60s; policy changes take effect within 60 seconds (a per-key override busts the cache immediately).
Metric: alerts that changed conduct
The headline governance KPI is not "how many alerts were generated" but how many changed the clinical decision. To measure it, the client records the decision taken on each surfaced alert.
Record a decision
POST /api/v1/interactions/alerts/decision (API-key auth, scope interactions:check):
curl -X POST https://api.saubit.com.br/api/v1/interactions/alerts/decision \
-H "X-API-Key: sm_live_..." \
-H "Content-Type: application/json" \
-d '{
"analysis_id": "a1b2c3...",
"alert_id": "drug_disease-1a2b3c4d5e",
"decision": "modified",
"changed_conduct": true,
"justification": "Swapped for a pregnancy-safe alternative"
}'
| Field | Description |
|---|---|
analysis_id | Id of the analysis (check/recheck) that produced the alert. |
alert_id | The alert id from clinical_alerts. |
decision | acknowledged (saw it, kept), overridden (overrode) or modified (changed conduct). |
changed_conduct | true if the alert actually changed the prescription/conduct. |
justification | Required when decision = "overridden". |
The server validates that the analysis belongs to the organization and backfills
alert_type/severity from the stored analysis.
Query the metric
The aggregated query (changed_conduct_rate = share of decisions where the alert changed
conduct, plus by-type/severity breakdowns) is an admin route documented in the Admin
Swagger (/docs/admin): GET …/organizations/{org_id}/alert-metrics. The recorded
decisions also feed the admin Dashboard/ROI.