POST /api/v1/interactions/check
Send a prescription with medications and, optionally, the patient's clinical context to receive a structured analysis with a stable contract, source traceability and a fail-safe policy.
Whenever possible, fill in patient_record.conditions and patient_record.current_medications.
This improves context and reduces the risk of false negatives.
Beyond the external sources (DDInter, openFDA, DailyMed, PubMed) and the local base, the
analysis applies a curated base of major/contraindicated interactions that works offline
and regardless of the input language. Textbook combinations (e.g. anticoagulant + antiplatelet,
nitrate + PDE5 inhibitor, statin + strong CYP3A4 inhibitor) are always flagged, even if an
external source is unavailable. These findings appear in interactions[] with
evidence[].source = "safety_net". A detected severe/contraindicated interaction is never
downgraded to UNKNOWN due to partial coverage.
Headers
X-API-Key: <your-key>Content-Type: application/json- Required scope:
interactions:check
Body
{
"medications": [
{"name": "Warfarin", "dose_mg": 5},
{"name": "Omeprazole", "dose_mg": 20}
],
"patient_record": {
"patient_reference": "PRONT-12345",
"current_medications": [
{"name": "Dipyrone", "dose_mg": 500}
],
"conditions": ["diabetes", "hypertension"],
"notes": "Optional. Avoid PII (national IDs, phone, email)."
},
"language": "en"
}
The dose_mg field accepts a number (2.5) or a string with comma/dot ("2,5", "2.5", "2,5mg").
Structured clinical contract (UCUM)
Beyond dose_mg, each medication accepts optional structured fields (backward-compatible).
When dose + dose_unit (UCUM) are provided and dose_mg is omitted, the engine derives mg
automatically:
| Field | Description |
|---|---|
dose + dose_unit | Value + UCUM unit: mg, g, mcg, ng, mg/kg, mg/m2 (derives mg; mg/kg uses weight, mg/m2 uses BSA). |
dose_form · concentration · duration | Dose form, concentration/presentation, duration. |
prn · continuous · indication | PRN, continuous use, clinical indication. |
dcb · atc · rxnorm · gtin | Drug codes (when available). |
Units not convertible to mass (UI/IU, mEq, mmol, %, drops, puffs, mL) are
accepted, but the maximum-dose check is not applied and a ⓘ warning is emitted (transparency).
{ "medications": [
{ "name": "Paracetamol", "dose": 2, "dose_unit": "g", "frequency": "6/6h", "dose_form": "tablet" },
{ "name": "Vancomycin", "dose": 15, "dose_unit": "mg/kg", "route": "IV" },
{ "name": "NPH insulin", "dose": 10, "dose_unit": "UI" }
] }
Conditions by ICD-10 code
patient_record.conditions accepts either free text ("renal impairment") or an ICD-10
code ("N18.3") — both resolve to the same internal tag and trigger the same validations
(e.g. renal_adjustment). Coverage is curated for the domains the engine actually evaluates
(renal, hepatic, heart failure, asthma/COPD, pregnancy, lactation, GI bleed, hyperkalemia,
hypertension, diabetes, long QT, Parkinson's, glaucoma, BPH, epilepsy) — it is not a full
ICD-10 table.
Pharmacogenomics (optional, opt-in)
patient_context.genotype accepts a gene -> phenotype map (e.g.
{"CYP2D6": "poor_metabolizer", "CYP2C19": "poor_metabolizer"}). Only evaluated when explicitly
sent — SauBit never infers or stores genotype. See pharmacogenomic under
Clinical validations for the covered gene-drug pairs.
Context transparency (clinical_context_assessment)
The response includes the clinical_context_assessment block, making explicit that "no
known interaction" ≠ "safe" when data is missing:
"clinical_context_assessment": {
"context_completeness": 0.5,
"missing_clinical_context": ["weight", "renal_function", "hepatic_function"],
"not_evaluated_domains": ["renal_adjustment", "drug_allergy"],
"residual_risk": "UNKNOWN"
}
context_completeness: fraction of context domains present (age, weight, renal/hepatic function, allergies, conditions).not_evaluated_domains: validations that could not run due to missing data.residual_risk: escalates toUNKNOWNwhen the prescription looks low-risk only because relevant data was not provided.
Response contract
analysis_status:complete | partial | failedprescription_risk_level:LOW | MODERATE | HIGH | CRITICAL | UNKNOWNprescription_score:0-100ornullconfidence_score:0.0-1.0clinical_relevance:high | moderate | low | unknownrecommendation_level:avoid_combination | monitor | adjust_dose | consult_professional | no_known_interaction | unknownnormalized_medications,interactions,unverified_pairssource_coverage: per-source status (queried, skipped, failed)analysis_metadata,warnings,safety_notice
Status rules
complete: all relevant pairs were checked and no required-source pending items.partial: there was an operational limit, a source pending item, orunverified_pairs.failed: the analysis could not be completed safely.
Response 200 (real example)
Real example of varfarina + aspirina (severe interaction detected by the curated base).
The contract is identical across routes (check, recheck, pharmacy/check).
{
"analysis_status": "complete",
"prescription_risk_level": "HIGH",
"prescription_score": 82,
"confidence_score": 0.9,
"clinical_relevance": "high",
"recommendation_level": "monitor",
"summary": "Complete analysis with interactions and required coverage met.",
"normalized_medications": [
{ "original_name": "varfarina", "normalized_name": "warfarin", "active_ingredient": "warfarin", "source": "inn_dictionary", "confidence": 0.72, "warnings": ["cmed_not_found_used_inn_fallback"] },
{ "original_name": "aspirina", "normalized_name": "aspirin", "active_ingredient": "aspirin", "source": "inn_dictionary", "confidence": 0.72, "warnings": ["cmed_not_found_used_inn_fallback"] }
],
"interactions": [
{
"drug_pair": ["varfarina", "aspirina"],
"severity": "GRAVE",
"score": 82,
"recommendation": "Avoid the combination; if already in use, monitor closely and refer for immediate professional review.",
"summary": "Important bleeding risk from combining an anticoagulant with an antiplatelet agent.",
"evidence": [
{ "source_name": "SauBit Safety Net", "source": "safety_net", "title": "Curated base of critical interactions", "url": null, "pmid": null, "doi": null }
]
}
],
"unverified_pairs": [],
"source_coverage": {
"ddinter": { "status": "success", "queried": true, "role": "primary", "items_found": 0, "pairs_checked": 1, "latency_ms": 76.6 },
"openfda": { "status": "success", "queried": true, "role": "primary", "items_found": 0, "pairs_checked": 1, "latency_ms": 760.7 },
"dailymed": { "status": "success", "queried": true, "role": "primary", "items_found": 0, "pairs_checked": 1, "latency_ms": 638.2 },
"pubmed": { "status": "success", "queried": true, "role": "supporting", "items_found": 2, "pairs_checked": 1, "latency_ms": 894.9 },
"crfmg": { "status": "success", "queried": true, "role": "supporting", "items_found": 0, "latency_ms": 938.8 },
"cmed_anvisa": { "status": "success", "queried": true, "role": "normalization", "items_found": 0, "latency_ms": 64.8 },
"admin_rag": { "status": "success", "queried": true, "role": "local_knowledge", "items_found": 3, "latency_ms": 3759.5 }
},
"analysis_metadata": {
"algorithm_version": "2026.05.07",
"model_name": "clinical_engine_v1",
"cache_hit": false,
"source_config_version": "2026.06.27"
},
"warnings": ["Internal admin base consulted with 3 relevant snippet(s)."],
"safety_notice": "This analysis is informational only and does not replace clinical judgment, patient evaluation, exams or specialist consultation.",
"analysis_id": "f063a5cc-cbf8-4b26-ac0e-10ca1bf9c393",
"cached": false,
"processing_time_ms": 5.91,
"timestamp": "2026-06-27T12:49:05Z"
}
Response 200 (no interaction)
When no known pair is found, interactions is empty and the risk is LOW/0:
{
"analysis_status": "complete",
"prescription_risk_level": "LOW",
"prescription_score": 0,
"confidence_score": 0.84,
"clinical_relevance": "low",
"recommendation_level": "no_known_interaction",
"summary": "Complete analysis with no known interactions for the checked pairs.",
"interactions": [],
"unverified_pairs": [],
"cached": false
}
Clinical validations (clinical_alerts)
Beyond drug-drug interactions (interactions), the response includes the
clinical_alerts array with extra deterministic validations that consider the
patient context (patient_record.conditions):
- Drug × disease (
type: "drug_disease"): contraindications/cautions by condition (e.g. NSAID in renal impairment, ACEi/ARB in pregnancy, beta-blocker in asthma). - Therapeutic duplication (
type: "duplicate_therapy"): same active ingredient or two drugs of the same class. - Maximum dose (
type: "max_dose"): estimated daily dose (dose_mg× frequency) above the recommended maximum. - Drug × allergy (
type: "drug_allergy"): the patient's known allergy or a possible cross-reaction (e.g. penicillin × cephalosporin) — usespatient_record.allergies. - Renal adjustment (
type: "renal_adjustment"): renally-eliminated drug with reduced renal function (renal condition or lowcreatinine_clearance). - Hepatic adjustment (
type: "hepatic_adjustment"): hepatically-metabolized drug in hepatic impairment (hepatic_functionor a hepatic condition). - Geriatrics (
type: "geriatric"): cautions in elderly ≥65 (Beers criteria) — usespatient_context.age. - Pediatrics (
type: "pediatric"): age-based contraindications (e.g. aspirin under 12 — Reye) — usespatient_context.age. - Pregnancy/Lactation: in addition to conditions, accepts the
pregnancy_statusandbreastfeeding_statusbooleans frompatient_context. - Drug × lab (
type: "drug_lab"): high K⁺ + hyperkalemia-raising drug, supratherapeutic INR + warfarin, prolonged QTc + QT-prolonging drug — usespatient_context.potassium/inr/qtc(orpatient_record.labs). - Food/Alcohol (
type: "food_alcohol"): per-drug advisories (metronidazole + alcohol, statin + grapefruit, MAOI + tyramine). - IV compatibility (
type: "iv_compatibility"): simultaneous-administration incompatibilities (ceftriaxone + calcium, phenytoin + dextrose). - Controlled substance (
type: "controlled_substance"): Brazilian ANVISA Portaria 344/98 classification (list A1-C5), the required prescription form and dispensing notes — informational, does not raiseprescription_risk_level. - Look-alike/sound-alike — LASA (
type: "lasa_medication"): drug on a curated list of commonly-confused pairs (e.g. hydralazine × hydroxyzine).MODERADOseverity when both pair members are in the same prescription;LEVE(reminder) when only one is present. - High-alert medication (
type: "high_alert_medication"): ISMP high-alert drug classes (anticoagulants, insulins, opioids, concentrated electrolytes, neuromuscular blockers, IV sedatives/vasopressors, chemotherapy) — reinforces double-check protocol, does not raise the computed risk. - Pharmacogenomic (
type: "pharmacogenomic"): only fires whenpatient_context.genotypeis explicitly sent (e.g.{"CYP2D6": "poor_metabolizer"}); covers CPIC-cited gene-drug pairs (CYP2D6×codeine/tramadol, CYP2C19×clopidogrel, CYP2C9/VKORC1×warfarin, TPMT×azathioprine). No genotype is ever inferred — strictly opt-in. - Black box warning (
type: "black_box_warning"): FDA boxed warning (openFDA), when one exists for the prescribed drug. Depends on an external source — subject to the same timeout/availability rules as the other external sources (seesource_coverage.black_box).
When a data point is not provided, the response declares in warnings (prefix ⓘ) which
validation was not evaluated — e.g. "renal adjustment not evaluated: renal function not
provided", "pediatrics/geriatrics not evaluated: age not provided".
These alerts also raise prescription_risk_level when severe/contraindicated.
"clinical_alerts": [
{
"type": "drug_disease",
"severity": "GRAVE",
"score": 82,
"title": "AINE pode agravar a função renal (nefrotoxicidade e retenção hídrica).",
"drugs": ["ibuprofeno"],
"condition": "renal_impairment",
"recommendation": "Evitar o uso nesta condição; considerar alternativa mais segura.",
"source": "clinical_validations"
}
]
Note: alert
title/recommendationtext is returned in Portuguese.
Aggregate polypharmacy (polypharmacy_risks)
Checking pairs is not enough: a prescription with many drugs can carry cumulative risk even
when each pair looks only moderate. The response includes polypharmacy_risks with the
aggregate loads over the full set (≥ 2 contributing drugs):
polypharmacy_qt (QT prolongation) · polypharmacy_bleeding · polypharmacy_anticholinergic ·
polypharmacy_cns_depression · polypharmacy_serotonergic · polypharmacy_nephrotoxic ·
polypharmacy_hyperkalemia · polypharmacy_fall (falls/hypotension).
Patient factors (QTc, potassium, age) escalate severity, and these loads also contribute to
the prescription_risk_level.
"polypharmacy_risks": [
{
"type": "polypharmacy_qt",
"load": "qt",
"severity": "GRAVE",
"score": 82,
"title": "Carga de prolongamento de QT: 3 fármacos contribuintes.",
"contributing_medications": ["amiodarona", "ondansetrona", "haloperidol"],
"patient_factors": { "qtc": 510, "potassium": 3.1 },
"recommendation": "Reduce the number of QT-prolonging drugs; monitor ECG/QTc..."
}
]
Alert governance (alert_governance)
To fight alert fatigue, the response includes the alert_governance block, which
records which alerts were surfaced, which were suppressed (and why), and which require
mandatory confirmation. Suppression is a presentation decision — it never lowers the
prescription_risk_level: a suppressed critical alert still drives the prescription risk.
Each clinical_alerts item now exposes:
id— stable alert identifier (drug-order independent), used to suppress/justify known alerts.requires_confirmation—truewhen the alert requires explicit confirmation before proceeding (by default,CONTRAINDICADOalerts).
Role views (physician × pharmacist)
Send the X-SauBit-Alert-View: medico or X-SauBit-Alert-View: farmaceutico header
(also accepts ?alert_view=) to get the role-appropriate view. In the pharmacist view,
diagnostic-reasoning alerts (e.g. drug_disease) leave the visible list
(suppression_reason: "out_of_view"), keeping the operational ones (allergy, duplication,
max dose, IV compatibility, drug × lab). Critical alerts are always surfaced.
"alert_governance": {
"applied": 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: below_min_severity, suppressed_type, policy_override (with
justification) and out_of_view. The policy (minimum severity, suppressed types, per-unit
rules, role views and justified overrides) is configured per organization/API key — see
Alert governance.
Error responses
| HTTP | detail example |
|---|---|
401 | API key required |
403 | API key inválida · IP não permitido para esta API key · Scope insuficiente para esta API key |
422 | validation list (missing required field, invalid type) |
429 | Rate limit excedido |
504 | A análise excedeu o tempo limite. Tente novamente em instantes. |
// 401 — missing X-API-Key header
{ "detail": "API key required" }
// 422 — body without "medications"
{
"detail": [
{ "type": "missing", "loc": ["body", "medications"], "msg": "Field required", "input": { "language": "en" } }
]
}
See the full catalog in Responses & Error Codes.
Diagnostic headers
X-SafeMed-Cache:HIT | MISSX-SafeMed-Agent-Mode:clinical_engine_v1X-SafeMed-Agent-Run:0 | 1X-SafeMed-Processing-Time-Ms,Server-Timing
Billing & cache
- Each call generates 1 billable transaction (uses the organization's
transaction_fee_brl). - Repeated identical clinical combinations may return
cached: true. Onlycompleteresponses are reused;partial/failedmay be recomputed. Dynamic fields likepatient_record.patient_referenceandnotesdo not enter the cache key.