DEV Community

Designing an API-First Value-Based Care Analytics Stack for MA Payers

If you build software for Medicare Advantage (MA) plans, "analytics" usually arrives as a vague requirement and leaves as a pile of nightly batch jobs and a BI dashboard nobody trusts. This post is about treating value-based care analytics as an engineering problem: data contracts, idempotent scoring, and auditability baked into the API surface.

The domain in one paragraph

Each member has diagnoses (ICD-10-CM codes) that map to Hierarchical Condition Categories (HCCs). HCCs, plus demographics, produce a Risk Adjustment Factor (RAF) under the CMS-HCC V28 model. RAF drives the plan's payment. So the analytics platform's job is to take coded encounters in, and emit defensible RAF and gap insights out - reproducibly.

1. Model the inputs as contracts, not files

The single biggest source of bad analytics is loose ingestion. Define a strict schema for encounters and enforce it at the edge:

{
  "member_id": "SYNTH-100245",
  "dos": "2026-02-11",
  "dx_codes": ["E11.9", "I50.32", "N18.4"],
  "source": "claim",
  "provider_npi": "0000000000"
}

Reject malformed records loudly instead of silently dropping them. A diagnosis that never made it into the pipeline is a RAF dollar that quietly disappears.

2. Make RAF scoring a pure function

RAF computation should be deterministic: given the same member inputs and the same model version, you get the same score. Pin the model version explicitly. You can build this yourself, but the hierarchy and coefficient logic is exactly what a scoring API exists to own. Calling one keeps your platform a pure pass-through:

import httpx, os

MODEL = "CMS-HCC-V28 Continuing Enrollee"

def compute_raf(member, model=MODEL):
    resp = httpx.post(
        "https://restapi.npidataservices.com/raf/api/v1/getScore",
        headers={
            "ApiKey": os.environ["RAF_API_KEY"],  # custom header, NOT Authorization: Bearer
            "Content-Type": "application/json",
            "accept": "application/json",
        },
        json={
            "model": model,
            "factor": "Community NonDual Aged",
            "age": member["age"],
            "gender": member["gender"],  # "MALE" | "FEMALE"
            # ICD-10-CM WITHOUT dots: "E11.9" -> "E119"
            "HCC_Codes": [c.replace(".", "") for c in member["dx_codes"]],
        },
        timeout=5.0,
    )
    resp.raise_for_status()
    return resp.json()["score"]["Total"]["Grand Total"]["RAF_Score"]

Two engineering points matter here. First, the hierarchy ("trumping" within an HCC family, where a more severe condition suppresses milder ones) is applied server-side - don't reimplement it, or you'll overstate the score by summing raw weights. Second, store the model string with every result; when you re-run history you must reproduce the score that was live at the time.

3. Treat suspecting as evidence-linked, not magic

A "suspect" condition should carry its supporting evidence (labs, meds, prior dx) as structured fields, so downstream consumers can rank by defensibility:

{
  "member_id": "SYNTH-100245",
  "suspect_hcc": "HCC38",
  "evidence": ["a1c>9.0", "rx:insulin", "prior_dx:E11.65"],
  "confidence": 0.82
}

Suspects without an evidence array are a code smell - and an audit liability.

4. Build audit simulation as a first-class endpoint

The most underrated feature: let the plan sample its own population and model RADV extrapolation exposure before CMS does. It's just sampling + documentation scoring + an extrapolation formula over synthetic data, but exposing it as an API turns "are we defensible?" into a number.

Why API-first wins

Dashboards are a presentation layer; the durable asset is a clean, versioned API that any tool can consume. If you want the product-level view of what MA payers actually need from this stack in 2026, this guide lays it out, and the broader risk adjustment analytics platform for payers shows how the pieces compose end to end. Build the contracts first. The dashboards get easy after that.

VBC Risk Analytics. Educational only - not coding, billing, or clinical advice; verify against the current CMS Rate Announcement. Synthetic data only.

Comments

No comments yet. Start the discussion.