Skip to content

Deploy ML Models: Predictions to FHIR RiskAssessments

Level: Intermediate

You have a model that outputs risk scores. To make those scores useful inside an EHR — visible to clinicians, dashboards, and quality measures — they have to become FHIR RiskAssessment resources. This tutorial shows the bridge: take the predictions your model already produced and emit spec-valid FHIR, one resource per patient, then optionally write them back to a live FHIR server.

Check out the full working example here!

Quick Start

The example runs offline with a pre-baked predictions dict — no model file, no server, no setup:

pip install healthchain
python cookbook/ml_risk_to_fhir.py
Built 3 RiskAssessment resources:
  Patient/1: HIGH (85%) → RiskAssessment/hc-1cdf...
  Patient/2: MODERATE (52%) → RiskAssessment/hc-5de5...
  Patient/3: LOW (9%) → RiskAssessment/hc-e1cb...

Bring Your Own Model

HealthChain is deliberately unopinionated about the model. XGBoost, a neural net, a scikit-learn pipeline — the only thing this step needs is the score your model already produced. Represent each prediction as a plain dict keyed by patient reference:

PREDICTIONS = {
    "Patient/1": {"probability": 0.85, "qualitative_risk": "high"},
    "Patient/2": {"probability": 0.52, "qualitative_risk": "moderate"},
    "Patient/3": {"probability": 0.09, "qualitative_risk": "low"},
}

How you get from raw FHIR to those features is your model's business — LOINC/SNOMED lookups, aggregation windows, imputation. HealthChain picks up once you have a score.

Emit FHIR

create_risk_assessment_from_prediction turns a prediction into a spec-valid RiskAssessment: the outcome as a coded CodeableConcept, the probability as probabilityDecimal, and the qualitative level as a coded qualitativeRisk. Loop it over your predictions and collect the results in a Bundle:

from healthchain.fhir import create_bundle, add_resource
from healthchain.fhir import create_risk_assessment_from_prediction

SEPSIS = {
    "code": "A41.9",
    "display": "Sepsis, unspecified organism",
    "system": "http://hl7.org/fhir/sid/icd-10",
}

bundle = create_bundle()
for subject, scores in PREDICTIONS.items():
    risk = create_risk_assessment_from_prediction(
        subject=subject,
        prediction={
            "outcome": SEPSIS,
            "probability": scores["probability"],
            "qualitative_risk": scores["qualitative_risk"],
        },
        comment="Generated by sepsis-risk model v1",
    )
    add_resource(bundle, risk)
Example RiskAssessment Resource
{
  "resourceType": "RiskAssessment",
  "id": "hc-1cdf...",
  "status": "final",
  "subject": { "reference": "Patient/1" },
  "prediction": [{
    "outcome": {
      "coding": [{
        "system": "http://hl7.org/fhir/sid/icd-10",
        "code": "A41.9",
        "display": "Sepsis, unspecified organism"
      }]
    },
    "probabilityDecimal": 0.85,
    "qualitativeRisk": {
      "coding": [{
        "system": "http://terminology.hl7.org/CodeSystem/risk-probability",
        "code": "high",
        "display": "High"
      }]
    }
  }]
}

For anything the helper doesn't cover — basis references to the observations behind a score, a coded method for the model, an occurrence timestamp — construct the RiskAssessment via fhir.resources directly.

Take It Live

Producing FHIR is the point because the resources can land in the EHR. Configure a FHIRGateway source and write each assessment back, where it becomes available to dashboards, reports, and downstream workflows:

from healthchain.gateway import FHIRGateway
from healthchain.gateway.clients import FHIRAuthConfig

gateway = FHIRGateway()
gateway.add_source("medplum", FHIRAuthConfig.from_env("MEDPLUM").to_connection_string())

for entry in bundle.entry:
    created = gateway.create(entry.resource, source="medplum")
    print(f"wrote RiskAssessment/{created.id}")

Prerequisites: a FHIR server with patient data. This example uses Medplum — see the FHIR Sandbox Setup guide for credentials, then add them to .env:

MEDPLUM_BASE_URL=https://api.medplum.com/fhir/R4
MEDPLUM_CLIENT_ID=your_client_id
MEDPLUM_CLIENT_SECRET=your_client_secret
MEDPLUM_TOKEN_URL=https://api.medplum.com/oauth2/token

The written resources are visible in the Medplum console — search "RiskAssessment" in the resource type search bar.

Next Steps

  • Real-time alerts: To surface a score at the point of care instead of persisting it, return it as a CDS Hooks card — see the CDS Hooks reference.
  • Add more FHIR sources: The gateway supports multiple sources — see the FHIR Sandbox Setup guide.
  • Go to production: Scaffold a project with healthchain new and run with healthchain serve — see From cookbook to service.