Skip to main content

Provider Attribution

Methods

Code on Github

Provider attribution assigns each person to the provider who most plausibly manages their primary care. Organizations use attribution for panel management, quality and cost performance measurement, contracting, network optimization, and outreach workflows.

The Tuva Provider Attribution preprocessing module implements a transparent, CMS-style primary-care attribution that runs using Tuva's standard claims, eligibility, and provider data assets. The assignment algorithm does not require external payer or custom assignment values. Enabling it does, however, also enable the provider_attribution Input Layer contract: the root project must define that model with the documented columns. Tuva normalizes that input and joins its payer/custom fields to member months alongside the calculated assignments. An empty model resolves the dbt dependency but does not satisfy Structural Data Quality's source-population requirement.

The logic is inspired by CMS attribution used in ACO/REACH contexts but generalized to work across all payers and claim types brought into Tuva. To improve coverage and analytics usability, we add two additional fallback passes (Steps 4–5) that expand the window and relax provider classification requirements only after the earlier CMS-like passes do not yield an assignment.

Specifically, the Core preprocessing module:

  • Classifies rendering NPIs as PCP, Specialist, or NPP using NPPES and the Medicare taxonomy crosswalk.
  • Identifies primary-care HCPCS services from claims.
  • Requires eligibility via member months within the lookback window.
  • Applies a five-pass methodology to find the best-fitting provider, adding broader fallback behavior only when needed.

Five-Pass Methodology

For each person, we evaluate providers in ordered passes and select the first pass that yields any qualifying provider(s). Within the chosen pass, providers are ranked by the highest summed allowed amount, then the highest number of visits. A null or zero allowed amount falls back to the paid amount for that claim line.

  1. 12-month PCP/NPP primary-care HCPCS
  2. 12-month Specialist primary-care HCPCS (only if Step 1 has no result)
  3. 24-month PCP/NPP primary-care HCPCS
  4. 24-month Primary-care HCPCS (any provider classification)
  5. 24-month Any rendering NPI (fallback when HCPCS-based classification fails)

Windows differ slightly by output:

  • Current: The last 12 or 24 calendar months ending on as_of_date. See Current Output Date Behavior and Configuration for defaults and overrides.
  • Yearly: Calendar-year windows (Jan..Dec) for the performance year, with expanded windows spanning Jan of Y-1 through Dec of Y (24 months) as a fallback.

The ranking table exposes all qualifying providers and the first pass each qualifies for. The assignment tables choose the top-ranked provider (rank = 1) or emit a labeled fallback when no assignable history exists.

Data-Source-Aware Attribution and Core Tables

Tuva-assigned attribution is calculated separately for each person_id and data_source. The yearly output has one row per person_id, data_source, and performance_year. The current output has one row per person_id, data_source, and as_of_date.

The yearly output is projected onto member-month records. Every row in core.member_month for the same person_id, data_source, and calendar year receives the same Tuva-assigned provider and attribution context. The same fields flow through to core.cost and core.utilization, which lets cost and utilization reporting use either externally supplied attribution fields or Tuva-assigned attribution fields.

Tuva attribution does not partition assignments by payer or plan. Existing externally supplied payer and custom attribution fields from input_layer.provider_attribution remain separate and unchanged.

When no eligible claims history exists, Tuva keeps the attribution bucket, step, and lookback context but leaves tuva_attributed_provider null.

Inputs and Dependencies

  • Provider attribution: input_layer.provider_attribution is cast in normalized__provider_attribution when provider attribution is enabled
  • Provider data: provider_data.provider is used directly by the attribution-specific provider classification step to validate rendering NPIs, keep individual providers, and attach specialty context
  • Claims: normalized medical claims from claims preprocessing
  • Eligibility: normalized eligibility exploded to Claims Preprocessing member months
  • Attribution windows: the package-owned member_month__month_spine, which contains every calendar month from January 1900 through December 2100. The published daily terminology.calendar asset is not a dependency.
  • Terminology and value sets:
    • cms_provider_attribution__primary_care_hcpcs_codes
    • cms_provider_attribution__provider_specialty_assignment_codes

The end-to-end flow is Input Layer provider attribution and provider data → Normalized Attribution → Claims Preprocessing Attribution → core.member_month, core.cost, and core.utilization.

Current Output Date Behavior and Configuration

The “current” scope uses a data-driven as_of_date to define its rolling 12- and 24-month windows:

  • Default: the maximum claim_end_date in the attribution claim set, if it is not null and is not a future date; otherwise the system date at runtime.
  • Override: set the dbt var provider_attribution_as_of_date to a YYYY-MM-DD value to pin as_of_date.

Examples:

Pin to a date:

dbt build --select tag:provider_attribution \
--vars '{"provider_attribution_as_of_date":"2025-10-01", "claims_enabled": true, "provider_attribution_enabled": true}'

Use the default date and enable both required domains:

dbt build --select tag:provider_attribution \
--vars '{"claims_enabled": true, "provider_attribution_enabled": true}'

Notes:

  • Models are enabled when both provider_attribution_enabled and claims_enabled evaluate true.
  • The “current” output runs for every person with at least one member month in the last 12 months ending at as_of_date. Persons without assignable history receive a labeled fallback row to keep the output grain of the tables at one row for every member with eligibility during the evaluation period.

Example SQL

Count Assigned by Step (Current)
select
assigned_step
, data_source
, count(*) as members
from claims_preprocessing.assigned_beneficiaries_current
group by
assigned_step
, data_source
order by
assigned_step
, data_source;
Provider Panel Size and Context (Current)
select
provider_id
, provider_bucket
, data_source
, count(*) as attributed_members
, sum(visits) as visits
, cast(sum(allowed_amount) as decimal(18,2)) as allowed_amount
from claims_preprocessing.assigned_beneficiaries_current
where provider_bucket <> 'no_eligible_history'
group by
provider_id
, provider_bucket
, data_source
order by
attributed_members desc;
Fallback Rate (Current)
select
data_source
, cast(sum(case when provider_bucket = 'no_eligible_history' then 1 else 0 end) as decimal(18,2))
/ nullif(count(*), 0) as fallback_rate
from claims_preprocessing.assigned_beneficiaries_current
group by
data_source;
Members With No Assignable History (Current)
select
person_id
, data_source
, as_of_date
from claims_preprocessing.assigned_beneficiaries_current
where provider_bucket = 'no_eligible_history';
Comparing Annual Attribution
with a as (
select
person_id
, data_source
, provider_id
from claims_preprocessing.assigned_beneficiaries_yearly
where performance_year = 2023
and provider_bucket <> 'no_eligible_history'
),
b as (
select
person_id
, data_source
, provider_id
from claims_preprocessing.assigned_beneficiaries_yearly
where performance_year = 2024
and provider_bucket <> 'no_eligible_history'
)
select
coalesce(a.data_source, b.data_source) as data_source
, coalesce(a.provider_id, 'none_prior') as prior_provider
, coalesce(b.provider_id, 'none_current') as current_provider
, count(*) as members
from a
full outer join b
on a.person_id = b.person_id
and a.data_source = b.data_source
where coalesce(a.provider_id, 'none') <> coalesce(b.provider_id, 'none')
group by
coalesce(a.data_source, b.data_source)
, coalesce(a.provider_id, 'none_prior')
, coalesce(b.provider_id, 'none_current')
order by
members desc;
Top-3 Ranked Providers Per Person

This surfaces why a specific provider was chosen by comparing step, allowed_amount, and visits across candidates.

with pr as (
select
person_id
, data_source
, scope
, performance_year
, as_of_date
, lookback_start_date
, lookback_end_date
, provider_id
, provider_bucket
, step as earliest_step
, step_description
, allowed_amount
, visits
, ranking
from claims_preprocessing.provider_ranking
where scope in ('current','yearly')
)
select
person_id
, data_source
, scope
, performance_year
, as_of_date
, lookback_start_date
, lookback_end_date
, provider_id
, provider_bucket
, earliest_step
, step_description
, allowed_amount
, visits
, ranking
from pr
where ranking <= 3
order by
person_id
, data_source
, scope
, ranking;

Sample Dashboard

Below is an embedded, interactive sample built from small CSVs bundled with the docs. Select a measurement period to see coverage, step mix, and top providers.

Loading sample dashboard…