Norvya Developers
Get a sandbox
API reference · Population & programmes

Population

Built — gated13 endpoints · 1 webhook event · OpenAPI tag population

Your whole panel at once. Aggregate counts (panel size, worklist priority tiers, rising risk, care gaps by measure) with small-cell suppression: counts below the minimum cell are withheld, and so is any cell that could be recovered by subtraction. Five eCQM-style quality measures and the Million Hearts ABCS as suppressed rates; these are NOT a certified eCQM calculation. A QRDA Category III file for 2026 with true counts, not submission-validated, audited. Asynchronous FHIR Bulk Data export of the panel, identified (each patient's sharing choices apply) or HIPAA Safe Harbor de-identified, audited per file. Programme reporting: HRSA UDS tables and the UDS+ FHIR submission for health centres, EPSDT screening counts (explicitly not a CMS-416 report), social-needs screening and referral counts, and HEDIS only where your organisation holds an NCQA licence and uploaded its own measure definitions (Norvya ships no HEDIS logic). Medical practices and programme clinics only.

Who calls it
Core for Federal (via primes), State & local government, Insurers (incl. Medicaid managed care); useful for Independent practices, Hospitals and health systems
Before production
No gate of its own; your market's “Before production” applies.
Sandbox
Build against synthetic members first. What each sandbox holds
Standards
eCQM-style measures (not certified); QRDA Category III; FHIR Bulk Data; UDS+

Markets that use this family

Scopes

ScopeAllows
population.aggregate.readRead aggregate counts for your whole active panel (worklist tiers, rising risk, care gaps); every count below the small-cell minimum is suppressed, no ids (medical practices only).
population.exportRun asynchronous FHIR Bulk Data exports of your panel, identified (each patient's sharing choices apply) or HIPAA Safe Harbor de-identified; audited per file (medical practices only).
population.measures.exportDownload the QRDA Category III file for the 2026 period: TRUE counts, not submission-validated, audited and refused when the audit log is down (medical practices only).
population.measures.readRead five eCQM-style quality measures and the Million Hearts ABCS as suppressed rates; NOT a certified eCQM calculation (medical practices only).

Endpoints

Aggregate picture of your whole panel, small cells suppressed

GET/v1/population/summarypopulation.aggregate.read

Panel size; patients by worklist priority tier and rising risk from the latest completed worklist run (named, with when it finished); open care gaps by measure and gap code for the current measurement year. Counts only: no member_refs, no ids, no free text. Every count from 1 to min_cell - 1 is withheld, and further cells are withheld when one could otherwise be recovered by subtracting from a total (the `suppression` field says how). Medical practices only (409 not_a_medical_practice).

Errors specific to this call

409not_a_medical_practice

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/summary" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "generated_at": "string",
  "min_cell": 5,
  "suppression": "string",
  "panel": {
    "active_members": {
      "count": 42,
      "suppressed": false
    },
    "adults": {
      "count": 42,
      "suppressed": false
    },
    "children": {
      "count": 42,
      "suppressed": false
    }
  },
  "worklist": {
    "run": {
      "id": 311,
      "status": "completed",
      "started_at": "2026-10-16T05:00:02Z",
      "finished_at": "2026-10-16T05:03:40Z"
    },
    "cohort": {
      "count": 42,
      "suppressed": false
    },
    "by_priority": {},
    "rising_risk": {
      "rising": {
        "count": 42,
        "suppressed": false
      },
      "not_rising": {
        "count": 42,
        "suppressed": false
      },
      "compared_with_run": {
        "id": 0,
        "finished_at": "string"
      },
      "definition": "string"
    },
    "ranking": "string"
  },
  "care_gaps": {
    "period_start": "string",
    "period_end": "string",
    "certified": false,
    "measures": [
      {
        "measure_id": "cms165_bp_control",
        "cms_id": "CMS165v14",
        "title": "string",
        "inverse": false,
        "open_gaps": {},
        "by_gap": []
      }
    ],
    "definition": "string"
  }
}

The response is the shape from the schema, not live data.

Quality measures and Million Hearts ABCS for your panel (not certified)

GET/v1/population/measurespopulation.measures.read

Five eCQM-style measures (CMS165v14, CMS122v14, CMS2v15, CMS138v14, CMS69v14) and the CDC Million Hearts ABCS, as rates whose numerators and denominators are withheld below min_cell (with complementary suppression). `certified` is always false: this is NOT a certified eCQM calculation (no CQL, no encounters; each measure lists `not_modelled`). The measure identifiers are valid for the 2026 period only (`spec_version_valid`). 422 invalid_period / period_too_long.

Parameters

period_startquery · string | null

YYYY-MM-DD (default: 1 January this year).

period_endquery · string | null

YYYY-MM-DD (default: 31 December this year); at most 366 days after period_start.

Errors specific to this call

422invalid_period

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/measures" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "generated_at": "string",
  "period_start": "string",
  "period_end": "string",
  "min_cell": 0,
  "suppression": "string",
  "certified": false,
  "certification_statement": "string",
  "encounter_basis": "active_panel_link",
  "spec_source": "string",
  "qrda3_available": false,
  "measures": [
    {
      "measure_id": "cms165_bp_control",
      "cms_id": "CMS165v14",
      "version": "v14",
      "version_specific_id": "string",
      "mips_quality_id": "236",
      "title": "string",
      "inverse": false,
      "spec_version_valid": false,
      "initial_population": {
        "count": 42,
        "suppressed": false
      },
      "denominator": {
        "count": 42,
        "suppressed": false
      },
      "denominator_exclusions": {
        "count": 42,
        "suppressed": false
      },
      "denominator_exceptions": {
        "count": 42,
        "suppressed": false
      },
      "numerator": {
        "count": 42,
        "suppressed": false
      },
      "performance_denominator": {
        "count": 42,
        "suppressed": false
      },
      "performance_rate": 0.0,
      "suppressed": false,
      "source_tiers": {},
      "age_unknown": {
        "count": 42,
        "suppressed": false
      },
      "populations": [
        {}
      ],
      "not_modelled": [
        "string"
      ]
    }
  ],
  "million_hearts": {}
}

The response is the shape from the schema, not live data.

QRDA Category III file for the 2026 period (true counts, not submission-validated)

GET/v1/population/measures/qrda3population.measures.export

The QRDA III document from Norvya's quality-measures engine: TRUE counts (a submission with suppressed counts would be invalid), labelled NOT SUBMISSION-VALIDATED, with no CMS EHR Certification ID (Norvya is not ONC-certified). X-Norvya-Validation-Gaps lists what keeps it from validating. 2026 performance period only (422 measure_version_unavailable). Every download is a chained PHI_EXPORT audit row and is refused (503) when the audit log is down.

Parameters

period_startquery · string | null

YYYY-MM-DD, within 2026.

period_endquery · string | null

YYYY-MM-DD, within 2026.

programquery · string

MIPS_GROUP | MIPS_INDIV

tinquery · string | null

9-digit TIN (default: the practice's).

npiquery · string | null

10-digit NPI (MIPS_INDIV).

All responses

200QRDA Category III XML (NOT SUBMISSION-VALIDATED).401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/measures/qrda3" \
  -H "Authorization: Bearer $TOKEN"

Start an asynchronous FHIR Bulk Data export of your panel

POST/v1/population/exportspopulation.export

Queues an export of every active patient of your practice as one NDJSON file per FHIR resource type (US Core-shaped, the same builders as Norvya's provider $export). deidentify=none: identified, each patient scoped by their sharing choices with your practice (and your own records under practice-of-record with a BAA), exactly as the provider export. deidentify=safe_harbor: HIPAA Safe Harbor (random per-export patient ids that do not link across exports, dates reduced to the year, birth year omitted at 90+, three-digit ZIP, free text removed, record numbers removed, DocumentReference left out), refused if any direct identifier survives. Poll the status_url or subscribe to population.export_completed. One export at a time per client (409 export_in_progress). Files expire after 24 hours. An identified export needs a BAA on file for your practice (409 population_export_baa_required); Safe Harbor does not. Kick-off, completion and every file download are chained PHI_EXPORT audit rows (503 when the audit log is down).

Request body

Content type: application/json

Errors specific to this call

409export_in_progress409population_export_baa_required503when

All responses

202Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s -X POST "https://norvya.fly.dev/v1/population/exports" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "types": [
      "string"
    ],
    "since": "string",
    "deidentify": "none"
  }'
Response 202
{
  "export_id": "9f1c2b7e4a5d4c3b8e7f6a5b4c3d2e1f",
  "status": "accepted",
  "status_url": "/v1/population/exports/9f1c2b7e4a5d4c3b8e7f6a5b4c3d2e1f"
}

The response is the shape from the schema, not live data.

Status and manifest of one export

GET/v1/population/exports/{export_id}population.export

200 with `status` while it runs and once it ends; the manifest (FHIR Bulk Data shape: output[{type, url, count}], requiresAccessToken, transactionTime, plus `deidentified`) once completed. 404 export_not_found for an export another client or organisation started; 410 export_expired / export_deleted.

Parameters

export_idpath · stringrequired

Errors specific to this call

200with410export_expired

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict410Gone422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/exports/$EXPORT_ID" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "export_id": "string",
  "status": "string",
  "progress": "string",
  "deidentify": "string",
  "created_at": "string",
  "expires_at": "string",
  "error": "string",
  "manifest": {
    "transactionTime": "string",
    "request": "string",
    "requiresAccessToken": false,
    "output": [
      {
        "type": "Observation",
        "url": "string",
        "count": 0
      }
    ],
    "error": [
      null
    ],
    "deidentified": false
  }
}

The response is the shape from the schema, not live data.

Cancel or delete an export

DELETE/v1/population/exports/{export_id}population.export

Cancels a running export or deletes a finished one's files. Idempotent (204 again for an export already deleted or expired).

Parameters

export_idpath · stringrequired

Errors specific to this call

204again

All responses

204Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s -X DELETE "https://norvya.fly.dev/v1/population/exports/$EXPORT_ID" \
  -H "Authorization: Bearer $TOKEN"
Response 204
No response body.

The response is the shape from the schema, not live data.

Download one NDJSON file of a completed export

GET/v1/population/exports/{export_id}/files/{name}population.export

application/fhir+ndjson, one resource per line. Every download is a chained PHI_EXPORT audit row, written BEFORE the file is returned and refused (503) when the audit log is down. 409 export_not_ready while it runs; 404 export_file_not_found; 410 once expired or deleted.

Parameters

export_idpath · stringrequired
namepath · stringrequired

Errors specific to this call

409export_not_ready404export_file_not_found410once

All responses

200One NDJSON file.401Unauthorized403Forbidden404Not Found409Conflict410Gone422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/exports/$EXPORT_ID/files/$NAME" \
  -H "Authorization: Bearer $TOKEN"

Social needs prevalence and closed-loop rates for your panel, small cells suppressed

GET/v1/population/social-needspopulation.aggregate.read

Screening rate, any unmet need, each domain's positive rate (housing, food, transportation, utilities), your referrals by status with the closed and needs-met rates, and members with a need you have not referred. Counts as cells, primary + complementary suppression; members who do not share their medical history are counted, never examined.

Parameters

startquery · string | null

YYYY-MM-DD (default: 365 days before end).

endquery · string | null

YYYY-MM-DD (default: today).

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/social-needs" \
  -H "Authorization: Bearer $TOKEN"

HRSA UDS tables Norvya can contribute to (UDS-aligned, not validated), small cells suppressed

GET/v1/population/udspopulation.measures.read

Tables 3A, 3B, 4, 6A and the 6B / 7 measures Norvya computes, with completeness and what is out of scope, from uds_reporting. Labelled "UDS-aligned, not validated in the EHBs/UDS+ test environment". PRIMARY small-cell suppression only (the staff screen's rule; `suppression` says why); true counts only through /population/uds/export.

Parameters

yearquery · integer | null

Calendar year (default: last year).

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/uds" \
  -H "Authorization: Bearer $TOKEN"

UDS tables with true counts (health centres; audited, fails closed)

GET/v1/population/uds/exportpopulation.measures.export

The staff export: TRUE counts (a UDS submission cannot carry suppressed cells). 409 uds_not_health_center unless your UDS settings declare a health centre. A chained PHI_EXPORT row first; 503 when the audit log is down.

Parameters

yearquery · integer | null
formatquery · string

json | csv

Errors specific to this call

409uds_not_health_center503when

All responses

200UDS tables, TRUE counts (UDS-aligned, not validated).401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/uds/export" \
  -H "Authorization: Bearer $TOKEN"

UDS+ patient-level FHIR export (health centres; audited, fails closed)

GET/v1/population/uds-pluspopulation.measures.export

fhir_bulk.uds_plus_export (the one FHIR builder): one Patient per UDS patient under a per-clinic token, the patient's clinical resources within their sharing choices, 42 CFR Part 2 resources always withheld, the identifier scan before release. Shaped to HRSA UDS+ FHIR IG 2.0.0 but NOT validated in HRSA's UDS+ test receiver (`profile_claimed` false). A chained PHI_EXPORT row first; 503 when the audit log is down; 500 export_identifier_detected if a direct identifier is found.

Parameters

yearquery · integer | null
deidquery · string

none (tokenised UDS+ shape: pseudonymous patient ids, coded demographics) | safe_harbor (additionally HIPAA Safe Harbor)

Errors specific to this call

503when

All responses

200UDS+ shaped FHIR NDJSON files + manifest (zip).401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/uds-plus" \
  -H "Authorization: Bearer $TOKEN"

EPSDT components due now for the children of your panel (not a CMS-416), suppressed

GET/v1/population/epsdtpopulation.measures.read

Children your practice holds (guardian-linked child profiles), how many share their record with you, and per EPSDT component how many have it due now on Norvya's schedule (epsdt.py). NOT a CMS-416: no eligibility months, no claims. Vision, hearing and oral health carry periodicity_asserted false (Norvya does not assert those ages).

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/epsdt" \
  -H "Authorization: Bearer $TOKEN"

HEDIS measures for NCQA licensees only (409 hedis_not_licensed otherwise)

GET/v1/population/hedispopulation.measures.read

No HEDIS specification logic is in Norvya (NCQA requires a licence for every use). A deployment that holds an NCQA licence (HEDIS_LICENSED) and uploaded its own measure definitions gets each one computed by the Norvya computation it binds to, as suppressed cells; otherwise 409 hedis_not_licensed (or hedis_no_definitions). Never certified.

Parameters

period_startquery · string | null
period_endquery · string | null

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/population/hedis" \
  -H "Authorization: Bearer $TOKEN"

Webhook events

Payloads carry no health data, only a reference to fetch with your token. Each event needs its scope as well as webhooks.manage. Signing and retries

EventScopeWhen
population.export_completedpopulation.exportA FHIR Bulk export you started finished (sent to that client only).

Generated from the Partner API router and developers_catalog.py. The router docstring is the source of each description.

↑ ↓ to moveEnter to open