Skip to main content

State rate code reference UI

Outcome

State-defined rate codes are managed via a UI with diff preview before commit. Rate refreshes happen without DB migrations.

Prerequisites

  • admin.manage for write/import.
  • config.read to browse.

What this owns

rcm_master.state_rate_code is master reference data — a per-state catalog that CodeResolver consults as the tier-2 pricing fallback when no fee schedule matches.

Pricing tierSource
Tier 1Contract-scoped fee schedule entry
Tier 2state_rate_code (managed here)
Tier 3Catalogue fallback

Routes

  • GET /api/v1/state-rate-codes — paginated list. Filters: state (comma list), serviceCategory, isActive, q (ILIKE on rate_code/description), effectiveOn (point-in-time), page, pageSize (capped at 200).
  • POST /api/v1/state-rate-codes/import/preview — body { csv }; returns { added, changed, retired, totalRows, errors } without writing.
  • POST /api/v1/state-rate-codes/import — body { csv, retire? }. Upserts on (state, rate_code, effective_from). With retire=true, deactivates active rows in the affected states that are missing from the CSV (sets is_active=false; data is preserved for audit, never deleted).

All three routes require admin.manage.

Operator playbook — refreshing a state's rates

  1. Open /admin/state-rate-codes → set the State filter to TX. Review the current snapshot. Inactive rows surface via the Status filter.

  2. Import CSV → drag-drop the new sheet (or paste CSV text). The header row must be:

    state,rate_code,description,service_category,unit_type,rate_amount_cents,effective_from,effective_to
  3. Preview diff → review Added / Changed / Retired sections. Field-level diffs are shown for each changed row.

  4. Decide on retirement. If this CSV is the authoritative source for the state, tick Deactivate the N retired rows on commit. Otherwise leave the box unchecked — Commit will then upsert without retiring anything.

  5. Commit → toast confirms {imported, updated, retired}. The list view refreshes.

Break-glass

If the UI is unavailable, the same CSV can be sent directly:

curl -X POST -H "content-type: application/json" \
-d "$(jq -Rn --rawfile csv /tmp/tx-rates.csv '{csv:$csv,retire:true}')" \
$RCM_CORE_URL/api/v1/state-rate-codes/import

/import/preview is also available as a no-op safety check before the write.

Schema reference

ColumnTypeNotes
state_rate_code_iduuid PK
statevarchar(2)
rate_codevarchar(20)
descriptionvarchar(255) NULL
service_categoryvarchar(50) NULLe.g. BH, OTP, ABA.
unit_typevarchar(20) NULLe.g. 15MIN, DAY, VISIT.
rate_amount_centsint NULL
effective_fromdate NOT NULL
effective_todate NULL
is_activebooleanUI sets false via retire=true.

Unique constraint: (state, rate_code, effective_from) — same key the import upserts on. Lookup index: (state, rate_code, effective_from, effective_to).

Five seeded states ship with the container (TX/CA/NY/FL/PA from master migration 023).

Validation

CheckExpected
Preview matches expected diffCounts agree with manual eyeball
Commit inserts/updates rowsMatch preview exactly
Retired rowsis_active=false, data preserved
CodeResolver for an existing rate codePicks state row when no fee schedule matches

Troubleshooting

SymptomCauseFix
Preview returns errors with row numbersBad header or row schemaFix the CSV; preview is non-destructive.
Retire count high unexpectedlyCSV missing rate codes that exist in DBConfirm the CSV is authoritative — uncheck retire to keep originals.
Pricing still uses fee schedule, not state codeTier-1 fee schedule still winsExpected when contract-scoped pricing applies.

Cross-references

Next

5.1 — Ingestion mapping editor