Accessibility Options

Implementation Guide

A complete reference for integrating the FHIR Risk Assessment Engine into your Medicare Advantage workflow

Quick Navigation

This guide walks you through the engine end-to-end:

1
Understand the Engine

Learn what the risk assessment engine does and why it matters for Medicare Advantage compliance.

Go to Section 1
2
Send Data to the Engine

Learn the accepted FHIR resource formats, required fields, and how to structure your API requests.

Go to Section 2
3
Interpret the Output

Understand every field in the API response, what each risk level means, and how to act on the results.

Go to Section 3
4
End-to-End Walkthrough

Follow a complete example from building a FHIR Bundle to reading the assessment results.

Go to Section 4

[1] Engine Purpose

What This Engine Does

The FHIR Risk Assessment Engine evaluates diagnosis codes submitted by Medicare Advantage organizations to identify codes that are likely to be flagged in CMS or OIG audits. It cross-references each diagnosis against supporting clinical evidence — encounters, medications, and procedures — to determine whether the code is adequately supported.

In plain terms: You send patient diagnosis data in FHIR format, and the engine tells you which codes look risky and why — before an auditor does.
Why It Matters

CMS recovers billions annually from Medicare Advantage plans through Risk Adjustment Data Validation (RADV) audits. The OIG has identified specific diagnosis categories that are frequently miscoded, leading to improper payments. This engine targets the four highest-risk categories:

Acute Stroke HCC 100

Stroke codes used in outpatient settings without an inpatient stay are a top audit target. Often "history of stroke" is the correct code.

Acute MI HCC 86

Acute heart attack codes without a matching inpatient admission within 60 days suggest potential miscoding as "old MI."

Embolism HCC 107/108

Embolism diagnoses without anticoagulant prescriptions within 90 days often indicate the condition is historical, not active.

Lung Cancer HCC 9

Active lung cancer codes without radiation, chemo, or surgery within 6 months suggest the patient may be in remission.

How It Works — At a Glance
Step What Happens
1 You submit FHIR resources (Conditions, Encounters, Medications, Procedures) via POST /api/assess
2 The engine validates the FHIR structure and extracts resources by type
3 Each Condition is checked against CMS/OIG high-risk filters (stroke, MI, embolism, lung cancer)
4 The engine looks for supporting evidence (encounters, medications, procedures) within defined time windows
5 Each diagnosis receives a risk level (high, moderate, or low) with a human-readable reason

[2] Sending Data to the Engine

API Endpoint
POST /api/assess
Content-Type: application/json
Accepted Input Formats

The engine accepts three input structures. Use whichever fits your system best:

A standard FHIR Bundle wrapping all resources in an entry array. This is the most common format used in FHIR integrations.

{
  "resourceType": "Bundle",
  "type": "collection",
  "entry": [
    {
      "resource": {
        "resourceType": "Condition",
        "id": "cond-001",
        "code": {
          "coding": [{
            "system": "http://hl7.org/fhir/sid/icd-10-cm",
            "code": "I63.9",
            "display": "Cerebral infarction, unspecified"
          }]
        },
        "subject": { "reference": "Patient/pat-001" },
        "onsetDateTime": "2024-09-15"
      }
    },
    {
      "resource": {
        "resourceType": "Encounter",
        "id": "enc-001",
        "class": { "code": "AMB" },
        "subject": { "reference": "Patient/pat-001" },
        "period": { "start": "2024-09-15" }
      }
    }
  ]
}

A simple JSON array containing FHIR resources directly.

[
  {
    "resourceType": "Condition",
    "id": "cond-001",
    "code": {
      "coding": [{
        "system": "http://hl7.org/fhir/sid/icd-10-cm",
        "code": "I21.3",
        "display": "STEMI of unspecified site"
      }]
    },
    "subject": { "reference": "Patient/12345" },
    "onsetDateTime": "2024-09-01"
  },
  {
    "resourceType": "Encounter",
    "id": "enc-001",
    "class": { "code": "IMP" },
    "subject": { "reference": "Patient/12345" },
    "period": { "start": "2024-09-01", "end": "2024-09-05" }
  }
]

A single FHIR resource (typically a Condition). Useful for quick single-code checks, but without supporting resources the engine cannot perform full validation.

{
  "resourceType": "Condition",
  "id": "cond-001",
  "code": {
    "coding": [{
      "system": "http://hl7.org/fhir/sid/icd-10-cm",
      "code": "C34.1",
      "display": "Malignant neoplasm of upper lobe, bronchus or lung"
    }]
  },
  "subject": { "reference": "Patient/67890" },
  "onsetDateTime": "2024-06-01"
}
FHIR Resource Reference

Each resource type plays a specific role in the risk assessment. The more supporting resources you include, the more accurate the assessment.

Resource Type Required? Key Fields Used For
Condition Required code.coding[].system, code.coding[].code, onsetDateTime, subject.reference The diagnosis code to evaluate
Encounter Optional class.code, period.start, subject.reference Validates inpatient stays for Stroke and MI rules
MedicationRequest Optional medicationCodeableConcept.coding[].code, authoredOn Validates anticoagulant prescriptions for Embolism rule
Procedure Optional code.coding[].code, performedDateTime or performedPeriod.start Validates treatment for Lung Cancer rule
Supported Code Systems

The engine recognizes the following ICD-10 code system URIs in Condition.code.coding[].system:

  • http://hl7.org/fhir/sid/icd-10
  • http://hl7.org/fhir/sid/icd-10-cm
  • http://www.cms.gov/Medicare/Coding/ICD10
Encounter Class Codes

The engine checks the Encounter.class.code field for inpatient context. Recognized values:

CodeMeaningTriggers Validation
IMPInpatient
ACUTEAcute Care
inpatientInpatient (alternate)
acuteAcute (alternate)
AMBAmbulatory / Outpatient

[3] Interpreting the Output

Response Structure

Every successful response follows this structure:

{
  "status": "success",
  "diagnosis_results": [
    {
      "code": "I63.9",
      "description": "Cerebral infarction, unspecified",
      "risk_level": "high",
      "reason": "One stroke diagnosis on physician claim, no inpatient/outpatient claim"
    },
    {
      "code": "I21.3",
      "description": "STEMI of unspecified site",
      "risk_level": "low",
      "reason": "No risk factors identified"
    }
  ]
}
Field-by-Field Breakdown
Field Type Description
status string "success" or "error". Always check this first.
diagnosis_results array One entry per Condition resource submitted. Each contains the assessment.
diagnosis_results[].code string The ICD-10 code that was evaluated (e.g., "I63.9").
diagnosis_results[].description string Human-readable name of the diagnosis.
diagnosis_results[].risk_level string One of "high", "moderate", or "low". See risk level guide below.
diagnosis_results[].reason string Explains why the code received this risk level. Actionable for coders and auditors.
Understanding Risk Levels
HIGH

Action Required

The diagnosis code matches a CMS/OIG high-risk pattern and lacks required supporting evidence. This code is likely to be flagged in an audit. Review the reason field and gather supporting documentation or correct the code.

MODERATE

Review Recommended

Some risk factors are present but not all high-risk criteria are met. Manual review is recommended to confirm the code is properly supported.

LOW

No Action Needed

No risk factors identified. The diagnosis code either does not fall into a high-risk category or has adequate supporting evidence. Likely to pass audit.

What Triggers Each Risk Level
Condition Category Flagged as HIGH When Reason Message
Acute Stroke (I63.x, G45.x) No inpatient encounter (class IMP/ACUTE) within ±7 days of onset "One stroke diagnosis on physician claim, no inpatient/outpatient claim"
Acute MI (I21.x, I22.x) No inpatient encounter within ±60 days of onset "No inpatient diagnosis within 60-day window for acute myocardial infarction"
Embolism (I26.x, I74.x) No anticoagulant medication (Warfarin, Apixaban, Rivaroxaban, Dabigatran) within ±90 days "No matching anticoagulant medication event"
Lung Cancer (C34.x) No radiation, chemotherapy, or surgery procedure within ±6 months (180 days) "No radiation, chemo, or surgery within ±6 months"
Error Responses

When something goes wrong, the response includes an error field instead of diagnosis_results:

{
  "status": "error",
  "error": "No data provided or invalid JSON format"
}
HTTP CodeMeaningCommon Cause
400Bad RequestMissing or malformed JSON, invalid FHIR structure, missing required fields
500Internal Server ErrorUnexpected server-side failure — contact support

[4] End-to-End Walkthrough

Let's walk through a complete example: a patient with an acute stroke diagnosis seen in an outpatient setting.

Step 1 — Build Your FHIR Bundle

Assemble the patient's Condition (the diagnosis) and any supporting Encounter data into a FHIR Bundle:

{
  "resourceType": "Bundle",
  "type": "collection",
  "entry": [
    {
      "resource": {
        "resourceType": "Condition",
        "id": "cond-stroke-01",
        "code": {
          "coding": [{
            "system": "http://hl7.org/fhir/sid/icd-10-cm",
            "code": "I63.9",
            "display": "Cerebral infarction, unspecified"
          }]
        },
        "subject": { "reference": "Patient/pat-100" },
        "onsetDateTime": "2024-09-15"
      }
    },
    {
      "resource": {
        "resourceType": "Encounter",
        "id": "enc-outpatient-01",
        "class": { "code": "AMB" },
        "subject": { "reference": "Patient/pat-100" },
        "period": {
          "start": "2024-09-15",
          "end": "2024-09-15"
        }
      }
    }
  ]
}
Step 2 — Send the Request
curl -X POST https://your-domain.com/api/assess \
  -H "Content-Type: application/json" \
  -d @bundle.json
import requests
import json

with open("bundle.json") as f:
    bundle = json.load(f)

response = requests.post(
    "https://your-domain.com/api/assess",
    json=bundle
)

results = response.json()
for dx in results["diagnosis_results"]:
    print(f"{dx['code']}: {dx['risk_level']} — {dx['reason']}")
const bundle = { /* your FHIR Bundle */ };

const response = await fetch("/api/assess", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(bundle)
});

const data = await response.json();
data.diagnosis_results.forEach(dx => {
  console.log(`${dx.code}: ${dx.risk_level} — ${dx.reason}`);
});
Step 3 — Read the Response

Because the only encounter is ambulatory (AMB) — not inpatient — the engine flags the stroke diagnosis as high risk:

{
  "status": "success",
  "diagnosis_results": [
    {
      "code": "I63.9",
      "description": "Cerebral infarction, unspecified",
      "risk_level": "high",
      "reason": "One stroke diagnosis on physician claim, no inpatient/outpatient claim"
    }
  ]
}
What to do next: The reason tells you the stroke diagnosis appeared without a supporting inpatient stay. A coder should verify whether the patient was actually admitted. If not, the code may need to be changed to a "history of" code (e.g., Z86.73).
Step 4 — Fix and Re-submit

If you add an inpatient encounter and re-submit, the risk level drops:

{
  "resource": {
    "resourceType": "Encounter",
    "id": "enc-inpatient-01",
    "class": { "code": "IMP" },
    "subject": { "reference": "Patient/pat-100" },
    "period": {
      "start": "2024-09-15",
      "end": "2024-09-20"
    }
  }
}

New response:

{
  "status": "success",
  "diagnosis_results": [
    {
      "code": "I63.9",
      "description": "Cerebral infarction, unspecified",
      "risk_level": "low",
      "reason": "No risk factors identified"
    }
  ]
}
Best Practices
  • Include all supporting resources. Encounters, medications, and procedures dramatically improve accuracy.
  • Set onsetDateTime on every Condition. Without it, time-window rules cannot fire.
  • Use standard code systems. Stick to the three recognized ICD-10 URIs listed in Section [2].
  • Batch your submissions. Send all of a patient's resources in one Bundle for complete assessment.
  • Review all HIGH results. Each comes with a specific, actionable reason.
Common Questions

The engine will still process them, but any high-risk code will be flagged because there is no supporting evidence to validate it. Include encounters, medications, and procedures for accurate results.

No. The /api/assess endpoint processes data in memory and returns results immediately. Nothing is persisted from direct API calls.

Yes. Include resources for multiple patients in a single Bundle. Each Condition will be assessed independently using the supporting resources available in the submission.

The engine supports FHIR STU3 and R4 resource structures. The key requirement is that resources use standard field names (code, coding, onsetDateTime, etc.).
Ready to Try It?

See the engine in action with our interactive demo or explore pre-built test scenarios.

Interactive Demo High-Risk Scenarios API Reference