Skip to main content

Claims Service — User Stories

Service: claims-service Story prefix: CLAIMS-US Last updated: 2026-04-18


CLAIMS-US-001 — Create Patient Coverage Record

FieldValue
EpicCLAIMS-EPIC-01
SummaryRegistration staff can add an insurance coverage record for a patient
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-008
Legacy FR refsFR-INS-001

Story: As a registration staff member, I want to add an insurance coverage record for a patient so that billing staff can use it for claim submission.

Acceptance Criteria:

Scenario: Successfully create a primary coverage record
Given I am authenticated as a registration staff member for tenant "acme"
And patient "pat_001" has no existing coverage records
When I POST /api/v1/coverages with valid payerId, subscriberId, priority "primary", effectiveFrom "2026-01-01"
Then the response status is 201
And the response contains a "coverageId" with prefix "cov_"
And the coverage status is "active"
And a "claims.coverage.created.v1" event is published to NATS

Scenario: Enforce priority uniqueness — second primary blocked
Given patient "pat_001" already has an active primary coverage
When I POST /api/v1/coverages with priority "primary" for the same patient
Then the response status is 422
And the error code is "COVERAGE_PRIORITY_CONFLICT"

Scenario: Cross-tenant isolation
Given tenant "other" has coverage "cov_other_01"
When I GET /api/v1/coverages/cov_other_01 as tenant "acme"
Then the response status is 404

Technical notes: RLS enforced on coverages table. Priority uniqueness enforced at application layer (not DB constraint) to allow deactivation path. FHIR Coverage resource generated and stored.

DoD: Automated tests passing, tenant isolation test in CI, FHIR Coverage golden fixture validated.


CLAIMS-US-002 — Update Coverage Record

FieldValue
EpicCLAIMS-EPIC-01
SummaryBilling staff can update coverage details such as effective dates and copay
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-009
Legacy FR refsFR-INS-002

Story: As a billing staff member, I want to update a patient's coverage record so that benefit details remain accurate.

Acceptance Criteria:

Scenario: Update effective end date
Given coverage "cov_001" is active for tenant "acme"
When I PATCH /api/v1/coverages/cov_001 with effectiveTo "2026-12-31"
Then the response status is 200
And the coverage effectiveTo is "2026-12-31"
And the coverage status is still "active"
And a "claims.coverage.updated.v1" event is published

Scenario: Deactivate coverage by setting end date in the past
Given coverage "cov_001" is active
When I PATCH /api/v1/coverages/cov_001 with effectiveTo "2025-12-31"
Then the coverage status changes to "inactive"
And a "claims.coverage.updated.v1" event is published

DoD: Update and deactivation scenarios covered by integration tests. FHIR Coverage updated.


CLAIMS-US-003 — Verify Patient Eligibility

FieldValue
EpicCLAIMS-EPIC-01
SummaryStaff can trigger real-time eligibility verification for a patient's coverage
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-010
Legacy FR refsFR-INS-003, FR-INS-004, FR-INS-005

Story: As a billing staff member, I want to verify a patient's insurance eligibility in real time so that I can confirm active benefits before an encounter.

Acceptance Criteria:

Scenario: Successful eligibility verification
Given coverage "cov_001" is active
And the payer eligibility adapter returns an active response
When I POST /api/v1/coverages/cov_001/eligibility-checks
Then the response status is 201
And the response contains "status": "active"
And the response contains deductibleMet and oopMet amounts
And a "claims.eligibility.verified.v1" event is published
And the result is cached until expiresAt

Scenario: Payer returns inactive coverage
Given the payer returns an inactive eligibility response
When I POST /api/v1/coverages/cov_001/eligibility-checks
Then the response status is 201
And "status" is "inactive"
And an alert is triggered for billing staff

Scenario: Expired eligibility blocks claim assembly
Given the most recent eligibility check for cov_001 has expiresAt in the past
When billing-service attempts to assemble a claim with cov_001
Then the response status is 422
And the error code is "ELIGIBILITY_EXPIRED"

DoD: Stub adapter used in tests. Real payer adapter tested separately in adapter integration tests.


CLAIMS-US-004 — Request Prior Authorization

FieldValue
EpicCLAIMS-EPIC-02
SummaryClinicians or billing staff can submit a prior authorization request to a payer
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-011
Legacy FR refsFR-INS-006

Story: As a billing staff member, I want to request prior authorization for a planned procedure so that the claim will not be denied for missing auth.

Acceptance Criteria:

Scenario: Successfully submit prior authorization request
Given coverage "cov_001" is active
When I POST /api/v1/authorizations with procedureCode CPT:27447 and diagnosisCodes ["M17.11"]
Then the response status is 201
And the authorization status is "pending"
And the authorizationId has prefix "auth_"

Scenario: Approved auth records authorization number
Given authorization "auth_001" is pending
When I PATCH /api/v1/authorizations/auth_001 with status "approved" and authorizationNumber "PA-2026-99999"
Then the authorization status is "approved"
And the authorizationNumber is stored
And validFrom and validTo are set

Scenario: Expired authorization blocks claim without valid auth
Given authorization "auth_001" has validTo in the past
When billing-service assembles a claim requiring this authorization
Then a warning is added to the claim scrubbing result

DoD: Auth creation, approval, and expiry scenarios covered. Auth linked to claim in scrubbing logic.


CLAIMS-US-005 — Record Authorization Decision

FieldValue
EpicCLAIMS-EPIC-02
SummarySystem records prior authorization decision from payer
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-012
Legacy FR refsFR-INS-007, FR-INS-008

Story: As the system, I want to record a payer's prior authorization decision so that the authorization status is reflected for claim assembly.

Acceptance Criteria:

Scenario: Authorization denied
Given authorization "auth_001" is pending
When the payer adapter returns a denial with reason "Not medically necessary"
Then the authorization status is "denied"
And denialReason is stored
And a notification is created for billing staff

Scenario: Authorization modified — partial units approved
Given I requested auth for 5 units of a procedure
When the payer approves 3 units
Then approvedUnits is set to 3
And the authorization status is "approved"

CLAIMS-US-006 — Assemble Claim from Encounter Charges

FieldValue
EpicCLAIMS-EPIC-03
Summarybilling-service can trigger claim assembly from finalized encounter charges
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-001
Legacy FR refsFR-CLM-001

Story: As the billing-service, I want to assemble a claim from finalized encounter charges so that it can be submitted to the patient's payer.

Acceptance Criteria:

Scenario: Successful claim assembly
Given encounter "enc_001" has finalized charges with CPT 99213 and diagnosis J06.9
And patient "pat_001" has active primary coverage "cov_001"
When billing-service POSTs /api/v1/claims with encounterId and chargeIds
Then the response status is 201
And a claimId with prefix "clm_" is returned
And claim status is "draft" (or "submitted" if autoSubmit: true)
And a "claims.claim.created.v1" event is published

Scenario: Duplicate claim rejected
Given claim "clm_001" was submitted for enc_001 within the dedup window
When billing-service assembles another claim for enc_001 with the same charges
Then the response status is 409
And the error code is "DUPLICATE_CLAIM"

Scenario: Terminology validation failure
Given a charge contains an invalid CPT code "99999"
When billing-service assembles the claim
Then the response status is 422
And the error code is "CODING_INVALID"
And the invalid code is identified in the error detail

DoD: Duplicate detection, coding validation, and tenant isolation tests passing.


CLAIMS-US-007 — Scrub Claim Before Submission

FieldValue
EpicCLAIMS-EPIC-03
SummaryPre-submission scrubbing validates required fields and coding rules
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-002
Legacy FR refsFR-CLM-002

Story: As a billing staff member, I want the system to scrub a claim before submission so that preventable denials are caught early.

Acceptance Criteria:

Scenario: Scrubbing catches missing rendering provider
Given a claim draft is missing renderingProviderId
When scrubbing runs
Then the claim status remains "draft"
And the error list includes "MISSING_RENDERING_PROVIDER"

Scenario: Scrubbing catches invalid diagnosis pointer
Given a claim line item points to diagnosis index 5 but only 2 diagnoses are present
When scrubbing runs
Then the error list includes "INVALID_DIAGNOSIS_POINTER" for the affected line

Scenario: Clean claim passes scrubbing
Given a claim with all required fields and valid codes
When scrubbing runs
Then the claim status transitions to "ready"
And the claim is eligible for submission

CLAIMS-US-008 — Submit Claim via X12 837

FieldValue
EpicCLAIMS-EPIC-04
SummaryClaims in "ready" state are submitted to clearinghouse as X12 837 EDI
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-003
Legacy FR refsFR-CLM-003

Story: As a billing staff member, I want claims submitted electronically as X12 837 so that they reach the payer through the clearinghouse.

Acceptance Criteria:

Scenario: Successful X12 submission
Given claim "clm_001" is in "ready" status
And the clearinghouse is available
When I POST /api/v1/claims/clm_001/submit
Then the claim status becomes "submitted"
And a clearinghouseRef is stored
And a "claims.claim.submitted.v1" event is published

Scenario: Clearinghouse unavailable — claim remains ready
Given the clearinghouse returns a 503
When submission is attempted
Then the claim status remains "ready"
And the error is logged
And an alert is raised for SRE
And the operation is retried up to 3 times with backoff

Scenario: Module not licensed
Given tenant "unlicensed" does not have the "ehr.claims" entitlement
When billing-service calls POST /api/v1/claims
Then the response status is 403
And the error code is "MODULE_NOT_LICENSED"

CLAIMS-US-009 — Process Claim Acknowledgement

FieldValue
EpicCLAIMS-EPIC-04
SummarySystem processes 999/277 acknowledgement from clearinghouse
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-004
Legacy FR refsFR-CLM-004

Story: As the system, I want to process acknowledgement responses from the clearinghouse so that claim status reflects acceptance or rejection.

Acceptance Criteria:

Scenario: Accepted acknowledgement
Given claim "clm_001" is "submitted"
When a 999/277 acceptance is received with payerClaimNumber "PAY-99999"
Then the claim status becomes "accepted"
And payerClaimNumber is stored
And a "claims.claim.accepted.v1" event is published

Scenario: Rejected acknowledgement
Given claim "clm_001" is "submitted"
When a 999 rejection is received with error code "AK5*R"
Then the claim status becomes "rejected"
And the rejection reason is stored
And billing staff are notified for correction

CLAIMS-US-010 — Ingest and Process ERA

FieldValue
EpicCLAIMS-EPIC-05
SummarySystem ingests X12 835 ERA and applies payment allocations to claims
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-005
Legacy FR refsFR-CLM-005, FR-CLM-006

Story: As the system, I want to ingest an ERA from the clearinghouse and apply the payment allocations so that claims reflect their paid status.

Acceptance Criteria:

Scenario: Full payment applied
Given claim "clm_001" is "accepted" with total billed $275
When an ERA is received with full payment $275 for clm_001
Then clm_001 status becomes "paid"
And a remittance record is created
And a "claims.claim.paid.v1" event is published
And a "claims.remittance.applied.v1" event is published

Scenario: Partial payment applied
Given claim "clm_001" is "accepted" with total billed $275
When an ERA assigns $150 to clm_001 with CO-45 adjustment
Then clm_001 status becomes "partial_paid"
And adjustment codes are stored

Scenario: ERA idempotency — duplicate ERA ignored
Given ERA "rem_001" has already been processed
When the same ERA is ingested again
Then the response is 409 with error "REMITTANCE_ALREADY_APPLIED"
And no duplicate allocations are created

Scenario: Transactional integrity
Given an ERA with allocations for 3 claims
When processing fails after the first allocation
Then no allocations are committed (all-or-nothing transaction)
And the ERA is re-queued for reprocessing

CLAIMS-US-011 — Generate ExplanationOfBenefit

FieldValue
EpicCLAIMS-EPIC-05
SummaryFHIR ExplanationOfBenefit resources are generated from remittance data
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-005
Legacy FR refsFR-CLM-005

Story: As a patient-portal consumer, I want to read FHIR ExplanationOfBenefit resources so that patients can understand their payment and benefit details.

Acceptance Criteria:

Scenario: EOB readable via FHIR endpoint
Given remittance "rem_001" has been applied to claim "clm_001"
When I GET /fhir/R4/ExplanationOfBenefit?patient=pat_001
Then the response contains a FHIR ExplanationOfBenefit resource
And the resource includes payment amount, adjustment codes, and benefit applied amounts

Scenario: EOB patient access restricted to own records
Given patient "pat_002" is authenticated via patient-portal role
When they request /fhir/R4/ExplanationOfBenefit?patient=pat_001
Then the response status is 403

CLAIMS-US-012 — Record and Track Claim Denial

FieldValue
EpicCLAIMS-EPIC-06
SummaryBilling staff can view denial details including CARC/RARC codes
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-006
Legacy FR refsFR-CLM-007

Story: As a billing staff member, I want to see denial details including reason codes so that I can decide whether to correct and resubmit or file an appeal.

Acceptance Criteria:

Scenario: Denial recorded from ERA
Given an ERA contains a denial for claim "clm_001" with CARC code "CO-97"
When the ERA is processed
Then clm_001 status is "denied"
And a DenialCase is created with denialCode "CO-97", denialReason, and appealDeadline
And a "claims.claim.denied.v1" event is published
And billing staff receive a notification

Scenario: Denial detail visible via API
Given claim "clm_001" is denied with denial "dnl_001"
When I GET /api/v1/claims/clm_001
Then the response includes denial detail with denialCode and appealDeadline

CLAIMS-US-013 — File Appeal and Resubmit Corrected Claim

FieldValue
EpicCLAIMS-EPIC-06
SummaryBilling staff can file an appeal for a denied claim or resubmit a corrected claim
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle
FR referencesFR-CLAIMS-007
Legacy FR refsFR-CLM-008

Story: As a billing staff member, I want to file an appeal or resubmit a corrected claim so that legitimate denials can be reversed.

Acceptance Criteria:

Scenario: File appeal
Given claim "clm_001" is denied and within the appeal deadline
When I POST /api/v1/claims/clm_001/appeal with appealNotes
Then the denial appeal status is "filed"
And clm_001 status is "appealed"
And a "claims.claim.appealed.v1" event is published

Scenario: Appeal after deadline is rejected
Given claim "clm_001" is denied and the appealDeadline has passed
When I POST /api/v1/claims/clm_001/appeal
Then the response status is 409
And the error code is "CLAIM_INVALID_STATE" with message "Appeal deadline has passed"

Scenario: Resubmit corrected claim
Given claim "clm_001" is denied with incorrect procedure code
When I POST /api/v1/claims/clm_001/resubmit with corrected procedureCode
Then a new claim "clm_002" is created with status "submitted"
And clm_002 references clm_001 as the replaced claim
And clm_001 status is updated to "closed"

CLAIMS-US-014 — AFG/UAE Payer Adapter

FieldValue
EpicCLAIMS-EPIC-07
SummaryClaims submitted to AFG MoPH and UAE DHA payers via jurisdiction-specific adapters
StatusTo Do
PriorityShould
Labelsservice:claims, domain:revenue-cycle, slice:S3
FR referencesFR-CLAIMS-013
Legacy FR refs

Story: As an Afghanistan or UAE deployment operator, I want claims routed to the correct local payer using the required format so that reimbursement is processed without manual EDI translation.

Acceptance Criteria:

Scenario: AFG tenant uses MoPH adapter
Given tenant "afg_hospital_01" is configured with submissionChannel "afg_moph"
When a claim is assembled and submitted
Then the AFGMoPHAdapter.submit() is called
And the claim is formatted according to AFG MoPH specifications

Scenario: UAE tenant uses DHA REST adapter
Given tenant "uae_clinic_01" is configured with submissionChannel "uae_dha"
When a claim is assembled and submitted
Then the UAEDHAAdapter.submit() is called
And the claim is formatted using UAE DHA REST format

Scenario: Core claim domain unchanged
Given either AFG or UAE adapter is configured
When the claim is processed
Then the ClaimRecord domain model is identical to a standard X12 deployment

CLAIMS-US-015 — Tenant Isolation Security Gate

FieldValue
EpicCLAIMS-EPIC-08
SummaryNo cross-tenant claim or coverage data is accessible
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle, type:security
FR referencesFR-CLAIMS-014
Legacy FR refsFR-CLM-001, FR-INS-001

Story: As the platform security model, I want claims and coverage data to be strictly tenant-isolated so that no tenant can access another tenant's financial or PHI data.

Acceptance Criteria:

Scenario: Cross-tenant claim not accessible
Given tenant "alpha" has claim "clm_alpha_01"
When tenant "beta" requests GET /api/v1/claims/clm_alpha_01
Then the response status is 404

Scenario: Cross-tenant FHIR query returns empty
Given tenant "alpha" has FHIR Coverage resources
When tenant "beta" requests GET /fhir/R4/Coverage?patient=pat_alpha_01
Then the response is an empty Bundle (200 with 0 entries)

Scenario: CI adversarial test gate
Given the tenant isolation integration test suite runs in CI
When any RLS regression is introduced via migration
Then the CI build fails and the deployment is blocked

CLAIMS-US-016 — PHI Audit Trail Completeness

FieldValue
EpicCLAIMS-EPIC-08
SummaryAll claim and coverage mutations generate immutable audit records
StatusTo Do
PriorityMust
Labelsservice:claims, domain:revenue-cycle, type:compliance
FR referencesFR-CLAIMS-015
Legacy FR refsFR-CLM-001

Story: As the compliance team, I want an immutable audit trail for every claim and coverage mutation so that we can demonstrate HIPAA-equivalent audit coverage.

Acceptance Criteria:

Scenario: Claim creation audited
Given billing staff creates a claim
Then an audit record is emitted to audit-service
And the record contains actorId, tenantId, claimId, patientId, and timestamp

Scenario: Coverage read by patient portal audited
Given a patient views their EOB via patient-portal-service
Then an audit record is emitted with patientId and "claims.eob.read" event type

Scenario: Audit coverage test in CI
Given the audit event coverage test suite runs in CI
Then every state-changing operation on claims and coverages has a corresponding audit assertion
And the build fails if any mutation lacks an audit event

DoD: Audit completeness CI test gate passing. Security reviewer sign-off obtained.