← Back
Home › Systems › Healthcare

revenue-cycle-management

Turns care delivered by a hospital or clinic into money received, from charges and coding to claims and payments.

Epic Resolute, Waystar, R1 RCM, Change Healthcare

Open the revenue-cycle-management pagerevenue-cycle-manage… Hdr histogramdata-structures: Log-linear bucketed histograms with bounded relative error for latency percentilesDead-letter queuedistributed: A side queue for messages that keep failing: after N delivery attempts or a permanent error, park them with the reason so the main queue keeps moving.›Deterministic simulationtestingdistributed: Run the whole system in one thread under a seeded scheduler with simulated network, disk and clock, so every failure replays from its seed.›Durable workflow executiondistributed: Ordinary code made crash-proof by logging every side-effect result and replaying the code from that log; activities run at least once, workflow logic must be deterministic.›Primary-backup replicationdistributed: One node orders all writes and ships them to standbys; the acknowledgement rule (none, one, all) sets what a crash loses, and an external arbiter decides who takes over.›Priority waterfall allocationeconomics: Applying incoming payments to fees, interest and principal in a defined priority orderDouble-entry ledgerfinance: Append-only journal of transactions whose postings sum to zero per currency; balances are derived, so money can only move, never appear or vanish.›Idempotency keysfinance: Client-chosen unique key per logical operation, stored with a request fingerprint and the final response, so retries replay the first result instead of acting twice.›Money representationfinance: Store money as integer minor units with an explicit currency exponent, compute in wider integers, and round once with a named rounding mode.›Order lifecycle state machinefinance: Order states (new, partial fill, cancel/replace, rejected, expired) with strict transitions and idempotent eventsReconciliationfinance: Match internal ledger records against bank and processor statements by reference, then amount and date window; every unmatched or mismatched item becomes an exception.›Process supervisionhardware-os: A parent process starts services, reaps their exits and restarts them under a delay and burst-limit policy, then stops them with SIGTERM before SIGKILL.›Calibrationmachine-learning: Make predicted probabilities match observed frequencies: measure with ECE, fix post hoc with temperature, Platt or isotonic scaling on held-out data.›Gradient-boosted decision trees(GBDT)machine-learning: Add small regression trees one at a time, each fitted to the loss gradient; histogram splits over 256 bins make it the strongest tabular default.›Linear and logistic regressionmachine-learning: Fit a weighted sum of features to a number (least squares) or a probability (logistic); the baseline every model must beat.›Named entity recognitionmachine-learning: Find the character spans of entities in text and give each a type, by labelling every token and decoding a valid label sequence.›Optical character recognition(text recognition)machine-learning: Find text lines in an image, read each crop with a CTC or attention model, then correct with a lexicon and score confidence for review routing.›Text classificationmachine-learning: Assign one or more labels to a text; climb a ladder from regexes to linear models on n-grams, embeddings, fine-tuned encoders and LLM prompts, only as far as the data and budget demand.›Transformer blockml-training: The decoder-only pre-norm block (attention plus MLP on a residual stream) behind every modern LLM, with exact shapes, parameter, FLOP and KV-cache counts.›Fhir resource modelnetworking: FHIR resources, references, search parameters and REST interactions for health data exchangeHl7v2 message parsingnetworking: HL7 v2 pipe-delimited segments, escaping, ACKs and MLLP framing for clinical interfacesTerminology code systemsnetworking: Code systems, value sets and concept maps (SNOMED CT, LOINC, ICD) with subsumption queriesX12 edi messagingnetworking: ANSI X12 and EDIFACT interchanges: segment/element delimiters, ISA/GS/ST envelopes, control numbers and 997/CONTRL acknowledgementsRecord linkagesearch-sort: Probabilistic record matching (Fellegi-Sunter) with blocking for patient or entity indexesCoverage-guided fuzzingsecurity: Mutate inputs, run an instrumented target, and keep any input that reaches a new code edge; feedback turns blind guessing into incremental search.›Mutual TLSsecurity: TLS where the client also proves a certificate key, so every connection carries a verified, rotatable machine identity.›Policy engines (PDP/PEP, Cedar,Rego, ReBAC)security: Decide allow/deny for (principal, action, resource, context) in a separate engine with default deny, forbid-wins and an auditable list of determining policies.›Row-level security(multi-tenant isolation in the…security: Append a policy predicate to every query inside the database, so a forgotten WHERE tenant_id = ? in application code cannot leak another tenant's rows.›Point-in-time recoverystorage: Periodic base backups plus a continuously archived change log let you rebuild the state as of any instant, then branch from there.›Business day calendarssystems: Holiday calendars, business-day adjustment rules (following, modified following) and settlement cyclesExponential backoff with jittersystems: Retry after delays that grow geometrically and are randomized, so failed clients spread out instead of hammering a recovering service in synchronized waves.›Graceful drainingsystems: On a termination signal, stop taking new work, finish in-flight work until a deadline, tell clients to reconnect elsewhere, then exit; clients resume from a token.›Rete rules enginesystems: Match many rules against changing facts by caching partial matches in a shared network, so each change only does the work it actually affects.›Cooperative schedulingconcurrency: Green threads/coroutines yielding at await points on a user-space schedulerFault injectiondistributed: Inject latency, errors, kills and partitions via proxies, netem or hooksRecord replaydistributed: Record every nondeterministic input (time, randomness, I/O, model and tool outputs, per-tick player input) and re-execute deterministically for debugging, replays and state rebuildDurable job queuedistributed: Persisted tasks that workers lease for a visibility timeout, ack on success and retry with backoff; at-least-once execution that survives worker and queue crashes.›Event sourcingfinance: Persist state changes as an append-only stream of immutable events per entity; current state is a fold over them, guarded by expected-version appends.›Hierarchical timing wheelsystems: Timers bucketed by expiry into wheels of slots, coarser wheels for far deadlines: O(1) insert and cancel for millions of timeouts.›Health checkingsystems: Decide per backend whether to route to it using probes and real-traffic errors, with consecutive-count hysteresis, capped ejection and a panic fallback.›Leasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›Cryptographic hashes (SHA-2,BLAKE3)security: Collision-resistant fingerprints of data; SHA-256 for interoperability, BLAKE3 for speed, HMAC/HKDF for keyed uses.›Largest remainder roundingfinance: Splitting an amount into parts that sum exactly (largest remainder / Hamilton) for allocations, splits and prorationsFIX protocol (session layer)finance: Tag=value order-entry protocol over TCP with per-direction sequence numbers, heartbeats and resend so sessions survive disconnects without losing or duplicating orders.›Hierarchical state machinesystems: Nest states so a substate inherits its parent's reactions and only overrides what differs, with ordered entry and exit actions along the path.›+ 33 moreTransparent interceptionnetworking: Redirect traffic to a local proxy (iptables TPROXY/REDIRECT, eBPF) without app changesSyscall interceptionsecurity: Intercept syscalls via ptrace, seccomp user notify or a user-space kernelOptimistic concurrency controlstorage: Run transactions without locks, record what they read, and validate at commit that nothing changed; fastest when conflicts are rare.›Slab allocatorsystems: Pre-carve pages into equal-size slots per size class with an in-place free list: O(1) alloc and free, no external fragmentation, cache-warm reuse.›Fencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›Message sequence numbersdistributed: A per-stream counter on every message lets the receiver detect loss, duplicates and reordering, ack cheaply and resume after reconnect with "send from N".›+ 17 moreEbpfhardware-os: Verified in-kernel programs on hooks (kprobes, XDP, tc) with mapsPtrace process controlhardware-os: Ptrace: attach, read and write registers and memory, single-step, intercept signalsSeccomp bpfsecurity: Syscall filtering with seccomp BPF programs+ 7 more

How it is built

Architecture overview

Revenue cycle management (RCM) is the system that turns care a hospital or clinic delivered into money received. A visit produces charges (what was done), clinicians' notes become codes (diagnoses and procedures in standard code sets), the codes and charges are assembled into a claim and checked against the payer's rules before it is sent, the payer answers with an acknowledgement and later an explanation of payment, the payment is posted against the claim, denials are worked or appealed, and whatever is left becomes a patient bill. The exchange formats are fixed by the HIPAA transaction standards (X12 837 for the claim, 835 for the remittance advice) (CMS: electronic billing). The one idea that makes it work is that a claim is a long-running case, not a message: it has a state, a deadline, and money attached at every step, so the system is built as a state machine with an exact ledger underneath, rules to prevent errors before they cost a denial, and models to decide where human attention pays most.

Parts:

 registration / eligibility ─► encounter (EHR: HL7 v2 / FHIR) ─► charges + documents
                                                        │
                       OCR + NLP + classifier ──► suggested codes ──► coder review
                                                        │
                                                        ▼
                    claim builder ──► scrubber (rules, payer edits) ──► ready to send
                                                                           │
          claim state machine / durable workflow ◄── acks (999, 277CA) ◄── clearinghouse ◄── 837
                 │       ▲                                                       │
                 │       └──── denial prediction, work queues, appeals          ▼ payer
                 ▼                                               remittance 835 + payment
          ledger (double entry) ◄── posting (waterfall) ◄────────────────────────┘
                 │
                 ▼ reconcile (835 vs bank deposits) ─► patient statement ─► collections

Two clocks matter. The front-end clock (minutes: eligibility, coding while the chart is fresh, scrubbing before the patient leaves) is where errors are cheapest to fix. The back-end clock (days to months: payer adjudication, appeals, patient payment) is where timeliness limits and cash flow are decided.

How the parts fit together

Charge capture and coding

A claim needs codes: diagnoses (ICD-10-CM, with ICD-10-PCS for inpatient procedures), procedures (CPT and HCPCS), and for inpatient stays a grouping into a diagnosis-related group. The code sets are published and versioned, and the right code depends on documentation, so coders read the chart and assign them. Computer-assisted coding shortens that read. Scanned documents (referrals, outside records) enter through OCR text recognition; Named entity recognition finds the clinically meaningful spans (a condition, a drug, a procedure, laterality); and Text classification assigns codes to a document as a multi-label problem. Published research frames it exactly so: CAML uses convolution and label-wise attention over clinical notes to predict codes (Mullenbach et al. 2018), and PLM-ICD applies pretrained transformers (Huang et al. 2022); both are trained on the public MIMIC data (PhysioNet). The Transformer block is the modern encoder for long notes, with the cost that notes exceed typical input lengths and must be chunked. The contract with the rest of the system: the model suggests codes with evidence spans and a confidence, a coder accepts or edits, and only accepted codes reach the claim. Mapping between local and standard codes needs Terminology and code systems (terminology-code-systems, no page yet): versioned code sets with effective dates, since a code valid on the date of service may not exist today.

Claim build and scrubbing

The claim builder assembles the 837: professional claims for physician services, institutional for hospitals. The structure is loops of billing provider, subscriber and patient, claim, and service lines, and the formats are X12 transactions (x12-edi-messaging (no page yet); see the X12 transaction set list). Before sending, the scrubber applies edits so errors are caught while the encounter is fresh: missing fields, invalid code or modifier combinations, mutually exclusive procedures (CMS publishes National Correct Coding Initiative edits: NCCI), authorization on file, and payer-specific rules. The edits are many, change every quarter, and differ per payer, which is the shape a Rete rules engine is built for: facts (claim, lines, payer, dates) go in and fired rules come out, with an explanation of each. Where the question is "who may do what, under which condition" (who may override an edit, who may write off a balance), a Policy engine separates the decision from the code. Deadlines are counted in business days and payer calendars, with holidays and weekends: Business-day calendars (no page yet). Timely-filing limits and appeal windows depend on them, and a miscounted day is lost revenue.

Submission and acknowledgement

Claims go through a clearinghouse to the payer. Each hop acknowledges: a syntax acknowledgement (X12 999) says the file was readable, and a claim acknowledgement (277CA) says each claim was accepted or rejected by the clearinghouse or payer (X12 list). The submission part has three jobs. Do not send twice: an Idempotency key per claim submission (claim id plus version) lets a retry after a timeout be recognised, so a lost acknowledgement does not become a duplicate claim. Retry wisely: transport failures retry with Exponential backoff with jitter, so a payer outage does not turn into a storm when it recovers. Park what cannot proceed: a file that fails repeatedly goes to a Dead-letter queue with the error, to be fixed and replayed by a person; the claim is not lost and does not block the queue. The claim's own states (built, scrubbed, submitted, accepted, in review, paid, denied, appealed, closed) form an Order lifecycle state machine (no page yet), with only allowed transitions and a recorded reason for each.

Claim work-flow

Between submission and closure, a claim waits: for the payer (days to weeks), for a missing medical record, for an appeal deadline, for the patient. Durable workflow execution holds that waiting state with timers: check status after 30 days, escalate when overdue, start an appeal when a denial arrives, route to a human queue with a due date. A restart of the system or a deploy does not lose any case, and every step is retried safely because the individual steps are idempotent.

Payment posting and ledger

The 835 remittance advice explains payment per claim and per line: amount paid, and adjustments with a group code (contractual obligation, patient responsibility, other) and a reason code. The same remittance has to be tied to the money: the 835 carries a trace number that matches the electronic funds transfer, so reconciliation can show that every dollar deposited maps to remittances and every remittance to a deposit (reassociation of payment and remittance is covered by the CAQH CORE operating rules). The posting logic follows three blocks. Money representation stores amounts as exact integer minor units with a currency, never as floating point, and rounds in one place. A Double-entry ledger records every change in balance as balanced entries (charge: receivable up, revenue up; payment: cash up, receivable down; contractual adjustment: allowance up, receivable down), so any balance can be explained and the books always sum to zero. Priority waterfall allocation (priority-waterfall-allocation, no page yet) decides how a payment is divided across service lines and between payer and patient responsibility in a fixed order (for example, line by line in the payer's stated order, then secondary insurance, then the patient), with leftover cents assigned deterministically. Then Reconciliation closes the loop: expected versus received, deposits versus postings, with exceptions (short payments, unmatched deposits, takebacks) as work items.

Denial and payment prediction

Not all claims are equal; models decide where to look. Denial prediction estimates, per claim, the chance the payer will deny it, so the scrubber or a biller can fix it before sending. The data is tabular and mixed (payer, code, modifiers, provider, place of service, authorization status, history): Gradient-boosted trees are the strong default, and Linear and logistic regression is the baseline and the choice where each factor must be explainable to a payer-relations team. The prediction is only useful if it can be used as a probability: Calibration maps the raw score so that "30 %" means about 30 % of such claims are denied, and then the threshold is chosen by cost (a claim is held for review when the expected loss exceeds the cost of a review), not by a fixed score. The same machinery supports likelihood-to-pay for patient balances, which drives statement timing and payment plans.

Patient identity and interfaces

Charges are posted to the right person and encounter. Registration systems create duplicates (name spellings, moves, newborns); Record linkage (no page yet) finds candidate matches, and a wrong link puts another person's balance on a statement, so uncertain links go to a human. The clinical systems send events: admit, transfer and charge posting arrive as HL7 v2 message parsing (no page yet) messages (ADT and DFT types are the usual ones), and newer integrations use the FHIR resource model (no page yet), which has Claim and ExplanationOfBenefit resources (Claim, ExplanationOfBenefit). Many hospitals and practices share one platform, so Row-level security is applied in the database, not just in the application: one tenant's claims are never visible to another's.

Platform and assurance

Money processes cannot lose data or double-act. Process supervision and Graceful draining keep workers alive across failures and deploys; Primary-backup replication and Point-in-time recovery protect the ledger and claim store and allow undoing a mistaken bulk posting; Mutual TLS secures the links to clearinghouses and hospitals. The code is checked by Deterministic simulation testing (timeouts between "sent" and "acknowledged", duplicate messages, crashes mid-posting) and Coverage-guided fuzzing of the X12 and HL7 parsers. An HDR histogram (no page yet) tracks processing latency per stage, such as scrub time and posting time, where long tails show up as delayed cash.

Data and control flow

One claim from charge to cash:

  1. The EHR sends the encounter and charges (HL7 v2 or FHIR); patient identity is resolved and the account opened.
  2. Documents are processed (OCR, entity extraction, code classification); the coder sees suggested codes with evidence and accepts or edits; the codes are stored with the code-set version.
  3. The builder creates the 837; the scrubber's rules fire; denial risk is scored; the claim is fixed, held or released.
  4. The claim is submitted with an idempotency key; acknowledgements (999, 277CA) arrive and move its state, rejects go to a work queue and failures to the dead-letter queue.
  5. The workflow waits for adjudication with a timer; the remittance (835) arrives, is parsed, and posted: payment, contractual adjustments, patient responsibility, each as ledger entries in one transaction.
  6. Denials create work items with reason codes and an appeal deadline (counted in business days); the rest of the balance moves to the patient statement. Cash is reconciled against bank deposits. Time goes to the waits (payer, patient), not to the processing; the front-end steps take seconds to minutes against weeks of payer time.

One remittance (835) posting: parse and validate, find each claim by its id (idempotent: a duplicate file posts nothing new), apply each line's payment and adjustments to the ledger in one transaction, allocate by the waterfall, flag mismatches (paid more or less than expected, unknown claim), and update the claim states.

Scaling

  1. Coding labour: the first bottleneck is people. Levers: model suggestions with confidence so that high-confidence charts are only audited, and low-confidence ones go to coders; work queues by specialty.
  2. Denial rework: the second. Levers: prevention (scrubber rules fed by denial analysis), prediction to focus effort, automated resubmission for fixable errors.
  3. File throughput: thousands of claims and remittances per day per hospital. Levers: batch by payer, partition by tenant, idempotent steps so that workers scale out and retries are safe.
  4. Ledger write rate: posting is many small transactions. Levers: one database transaction per remittance, partition by account, balances as derived views.
  5. Payer variance: each payer behaves differently. Levers: per-payer adapters and rules, per-payer rate limits and backoff, latency and denial dashboards per payer.
  6. Model drift: payer behaviour changes. Levers: scheduled retraining, calibration checks against recent outcomes, and a human fallback.

Failure modes

FailureSymptomCauseFix
Duplicate claimpayer denies as duplicate, or pays twiceretry after a lost acknowledgement without an idempotency keyidempotency key per submission, claim versions for corrected claims
Wrong code accepteddenials for medical necessity, audit riskan auto-suggested code accepted without reviewevidence span shown, confidence threshold, sampling audits of auto-accepted codes
Outdated code setrejects for invalid codea code used outside its effective datesversioned code sets by date of service, scrubber edit on validity
Missed filing deadlinedenial that cannot be appealeddeadline miscounted (holidays, weekends) or the case stalledbusiness-day calendar per payer, workflow timers, aged-claim reports
Payment applied wronglypatient billed for an insurer's share, balances do not sumallocation order wrong, rounding lost centswaterfall rules in one place, integer money, ledger balance checks
Unreconciled depositscash in the bank with no matching remittancemissing 835, wrong trace numberreconcile deposit to remittance by trace number, exceptions queue
Uncalibrated denial modeltoo many holds or too few catchesscores read as probabilities without calibration, driftcalibrate on recent data, choose the threshold by cost, monitor
Wrong patient linkstatement or payment on the wrong persona loose match in registrationhigh-threshold linking, review queue, undo with history
Poison filequeue blocked by a malformed remittanceparser error on one filedead-letter queue, replay after a fix, fuzzed parsers
Tenant data exposureone hospital sees another's claimsapplication-only filteringrow-level security in the database

Real implementations

namewhat it isrepolicencenotes
OpenEMRopen EHR with billing that produces X12 claim filesopenemr/openemrGPL-3.0the open system closest to a full small-practice RCM
pyx12Python X12 validator and translator, including HIPAA transaction setsazoner/pyx12see repofor parsing and validating 837 and 835 files
HAPI FHIRJava FHIR implementation with Claim and ExplanationOfBenefit resourceshapifhir/hapi-fhirApache-2.0for the FHIR side of the interface
Medplumopen FHIR-based healthcare platformmedplum/medplumApache-2.0a platform, not a billing system
medspaCyclinical NLP built on spaCymedspacy/medspacyMITa building block for entity extraction from notes
CMS NCCI editspublished code-pair edits used by scrubberscms.govpublicquarterly updated reference data
CAQH CORE operating rulesrules for transaction timing and contentcaqh.orgpublic rulese.g. for acknowledgements and remittances
Epic Resolute, Waystar, R1 RCM, Change Healthcare (Optum)provider and vendor revenue-cycle platforms and clearinghousesepic.com, waystar.com, optum.comproprietarynamed in the goal; no public source

The alternative: for a small practice, the billing module of an EHR plus a clearinghouse portal is enough, and the prediction and workflow parts are left out. For pharmacy the claim goes in real time as an NCPDP transaction rather than as an 837 file, see the pharmacy management system page. For private-pay care (no payers) the whole claim and remittance side disappears, leaving statements, the ledger and collections.

Sources

  • CMS, HIPAA electronic billing and transaction standards: cms.gov.
  • X12 transaction set list (837, 835, 277, 999): x12.org.
  • CMS, National Correct Coding Initiative edits: cms.gov; CMS, ICD-10: cms.gov.
  • CAQH CORE operating rules: caqh.org.
  • J. Mullenbach et al., Explainable Prediction of Medical Codes from Clinical Text, NAACL 2018: arXiv:1802.05695.
  • C.-W. Huang, S.-C. Tsai, Y.-N. Chen, PLM-ICD: Automatic ICD Coding with Pretrained Language Models, 2022: arXiv:2207.05289.
  • MIMIC-III clinical database: physionet.org.
  • HL7 FHIR: Claim, ExplanationOfBenefit.

Similar systems

Share the most building blocks with this one.