Claims Service — Migration Plan
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · SERVICE_OVERVIEW · DOMAIN_MODEL
1. Background
The claims-service consolidates two legacy modules per ADR (see SERVICE_OVERVIEW):
| Legacy module | FR prefix | What it owned |
|---|---|---|
ADMIN-CLAIMS | FR-CLM-* | Claim assembly, scrubbing, submission, denial/appeal |
ADMIN-INS | FR-INS-* | Coverage management, eligibility verification, prior authorization |
FR mappings: all FR-CLM-* and FR-INS-* references are re-numbered as FR-CLAIMS-*. Legacy refs preserved in the FR mapping table for traceability.
2. Migration Scope
| Migration concern | Detail |
|---|---|
| Legacy billing / claims system data | Existing claim records (draft, submitted, paid, denied) migrate to claims.claim_records |
| Legacy insurance / coverage records | Patient coverage records migrate to claims.coverages |
| Prior authorization records | Migrate to claims.prior_authorizations |
| Remittance / ERA records | Historical ERAs migrate to claims.remittance_records |
| Payer onboarding configs | Legacy payer configuration (EDI ISA/GS segments, API auth) migrates to claims.payer_configs |
| FHIR financial resources | Legacy FHIR Claim and Coverage IDs re-mapped; redirect table active for 90 days |
3. Migration Phases
Phase 0: Pre-Migration Audit
- Audit legacy ADMIN-CLAIMS and ADMIN-INS databases for record counts, status distributions, schema gaps.
- Identify claims in ambiguous legacy states (e.g.,
PENDINGwith no payer response > 90 days) — categorize assubmittedwith flagmigration_stale. - Identify coverage records with overlapping effective dates for same patient/payer — resolve priority conflicts per
CoveragePriorityrules. - Catalog all legacy payer EDI configurations (ISA/GS segment values, X12 version, SFTP paths).
- Identify any claims linked to encounters that no longer exist in scheduling-service (orphaned claims).
Phase 1: Schema Deployment
- Deploy claims-service schema (
claimsPostgreSQL schema) alongside legacy schema. - Run initial Drizzle migrations — schema structure only, no data movement.
- Validate RLS policies in staging with synthetic claim data.
- Validate payer EDI adapter configuration import.
Phase 2: Coverage and Payer Onboarding Migration (Batch)
Coverage records are migrated first as they are referenced by claims:
-- Migrate legacy coverage / insurance records
INSERT INTO claims.coverages (
id, tenant_id, patient_id, payer_id, subscriber_id,
group_number, plan_name, priority, relationship,
effective_from, effective_to, copay_amount, copay_currency,
deductible_amount, status, created_at, updated_at
)
SELECT
'cov_' || legacy_ins.id,
legacy_ins.tenant_id,
legacy_ins.patient_id,
legacy_ins.payer_id,
legacy_ins.member_id,
legacy_ins.group_number,
legacy_ins.plan_description,
CASE legacy_ins.priority WHEN 1 THEN 'primary' WHEN 2 THEN 'secondary' ELSE 'tertiary' END,
LOWER(legacy_ins.relationship_to_subscriber),
legacy_ins.coverage_start_date,
legacy_ins.coverage_end_date,
legacy_ins.copay_amount,
COALESCE(legacy_ins.currency, 'AFN'),
legacy_ins.deductible_amount,
CASE WHEN legacy_ins.coverage_end_date IS NULL OR legacy_ins.coverage_end_date > NOW() THEN 'active' ELSE 'inactive' END,
legacy_ins.created_at,
legacy_ins.updated_at
FROM legacy_insurance.policies legacy_ins;
Payer config onboarding migration:
-- Seed payer configurations from legacy EDI settings
INSERT INTO claims.payer_configs (
id, tenant_id, payer_id, payer_name, submission_channel,
isa_sender_id, isa_receiver_id, gs_sender_id, gs_receiver_id,
sftp_host, sftp_path, is_active
)
SELECT
gen_random_uuid(), legacy.tenant_id, legacy.payer_code, legacy.payer_name,
CASE WHEN legacy.submission_mode = 'EDI' THEN 'x12_837'
WHEN legacy.submission_mode = 'API' THEN 'payer_rest'
ELSE 'x12_837' END,
legacy.isa_sender, legacy.isa_receiver,
legacy.gs_sender, legacy.gs_receiver,
legacy.sftp_hostname, legacy.sftp_upload_path, TRUE
FROM legacy_claims.payer_settings legacy;
Phase 3: Claim Records Migration (Batch)
-- Migrate historical claims
INSERT INTO claims.claim_records (
id, tenant_id, patient_id, encounter_id, coverage_id,
bill_provider_id, rendering_provider_id,
service_date_from, service_date_to,
diagnosis_codes, total_billed, status,
submission_channel, clearinghouse_ref, payer_claim_number,
version, created_at, updated_at
)
SELECT
'clm_' || legacy.claim_id,
legacy.tenant_id,
legacy.patient_id,
legacy.encounter_id,
'cov_' || legacy.coverage_id,
legacy.billing_npi,
legacy.rendering_npi,
legacy.service_date_from,
legacy.service_date_to,
to_jsonb(legacy.icd_codes),
legacy.billed_amount,
CASE legacy.claim_status
WHEN 'DRAFT' THEN 'draft'
WHEN 'SUBMITTED' THEN 'submitted'
WHEN 'ACCEPTED' THEN 'accepted'
WHEN 'PAID' THEN 'paid'
WHEN 'DENIED' THEN 'denied'
WHEN 'APPEALED' THEN 'appealed'
WHEN 'CLOSED' THEN 'closed'
ELSE 'draft'
END,
'x12_837',
legacy.clearinghouse_ref,
legacy.payer_claim_number,
1,
legacy.created_at,
legacy.updated_at
FROM legacy_claims.claims legacy;
Migration scripts summary:
| Step | Script | Idempotency |
|---|---|---|
| 1. Migrate payer configs | scripts/migrate-payer-configs.ts | Upsert on payer_id + tenant_id |
| 2. Migrate coverage records | scripts/migrate-coverages.ts | Upsert on id |
| 3. Migrate prior authorizations | scripts/migrate-prior-auths.ts | Upsert on id |
| 4. Migrate claim records | scripts/migrate-claims.ts | Upsert on id |
| 5. Migrate remittance records | scripts/migrate-remittances.ts | Upsert on id |
| 6. Migrate denial cases | scripts/migrate-denials.ts | Upsert on id |
| 7. Validate row counts | scripts/validate-claims-migration.ts | Count comparison report |
| 8. Generate FHIR ID redirect manifest | scripts/generate-fhir-claims-redirect.ts | ID map CSV |
Phase 4: Batch Claim Re-Processing
Some legacy claims may be in a PENDING / UNKNOWN status without a definitive payer response. These require re-processing:
- Query all migrated claims with
migration_staleflag wherestatus = 'submitted'andsubmittedAt < NOW() - 90 days. - Run eligibility re-check via
POST /v1/claims/:id/recheck-eligibility. - For claims > 1 year old with no response, mark as
closedwith reasonMIGRATION_NO_RESPONSEafter clinical billing review. - For re-submittable claims (within payer filing limit): create corrected claim and submit via new adapter.
Phase 5: Payer Onboarding Validation
For each payer in claims.payer_configs:
- Send test X12 270 (eligibility request) and verify 271 response.
- Send test X12 837 (claim) in test mode and verify 999/277 acknowledgement.
- Sign off from billing operations team before activating payer in production.
Phase 6: Cut-Over
| Step | Action |
|---|---|
| Stop writes to legacy claims system | Feature flag LEGACY_CLAIMS_WRITE=false |
| Run final delta sync (records created after Phase 3 snapshot) | Delta migration scripts with timestamp filter |
| Validate record counts (0 discrepancy threshold) | Automated script |
| Switch read and write traffic to claims-service | Kong route weight: 100% new service |
| Activate FHIR Claim and Coverage resources via interop-service | New R4 FHIR IDs; legacy IDs → 301 redirect for 90 days |
| Archive legacy system | Set replicas to 0; DB read-only for 90 days |
4. Payer Onboarding Process (New Payers Post-Migration)
After GA, new payer onboarding follows this flow:
5. Coverage Record Import from Existing Insurance Databases
For tenants with pre-existing patient insurance data in external systems:
| Import method | Use case | API / Tool |
|---|---|---|
| CSV batch import | Large-scale coverage import (> 1000 records) | POST /v1/coverages/batch-import (multipart CSV) |
| FHIR Coverage resource import | FHIR-capable source systems | POST /fhir/R4/Coverage via interop-service |
| Manual entry | Small practices, individual records | POST /v1/coverages standard REST |
| X12 834 enrollment feed | Group insurance enrollment files | Adapter in interop-service (backlog) |
CSV import template columns: patient_id, payer_id, subscriber_id, group_number, plan_name, priority, relationship, effective_from, effective_to, copay_amount, currency.
6. Rollback Plan
| Trigger | Rollback action |
|---|---|
| Migration count mismatch > 0.1% | Halt cutover; investigate delta; re-run migration |
| Claim submission failure rate > 0.5% post-cutover | Kong route back to legacy; page billing ops |
| Payer EDI adapter producing malformed 837 | Disable affected payer config; page integration team |
| FHIR Coverage ID mismatch in patient portal | Re-run FHIR redirect manifest; extend redirect window |
Legacy DB preserved read-only for 90 days post-cutover. Rollback runbook: infra/runbooks/claims-migration-rollback.md.
7. Legacy FR Mapping
| New FR | Legacy FR | Source module |
|---|---|---|
| FR-CLAIMS-001 | FR-CLM-001 | ADMIN-CLAIMS |
| FR-CLAIMS-002 | FR-CLM-002 | ADMIN-CLAIMS |
| FR-CLAIMS-010 | FR-INS-001 | ADMIN-INS |
| FR-CLAIMS-011 | FR-INS-002 | ADMIN-INS |
| FR-CLAIMS-012 | FR-INS-003 | ADMIN-INS |
8. Open Questions
- Confirm payer filing limit rules per country (AFG/UAE) — affects re-processing decision for stale claims.
- X12 834 enrollment feed adapter scope — confirm if needed for M1 or deferred.