Resources & Tech · EMR Solution

One patient table. No bridge, no sync, no reconciliation.

The EMR is a second front end over the same FastAPI backend and the same MySQL schema the network portal uses. There is no EMR patient table, no identity mapping table, no nightly reconciliation job and no interface engine in the middle. That is the whole architectural bet: an allergy recorded in the chart is visible to a delegation reviewer immediately, because it is the same row.

Overview

The engineering record behind a clinical EMR that reads and writes the same record the network runs on.

Technology stack by layer
LayerWhat runs thereWhy
EMR frontendNext.js 14 App Router, React 18, TypeScript, Tailwind — a separate application from the network portalTwo apps, two dependency sets, no shared component package. The EMR builds its own presentational primitives per screen rather than importing the portal's library, so a change to a portal button cannot alter a clinical screen.
Note editorTiptap, with link, image and placeholder extensionsStructured rich text for the visit note, with the extensions the clinical note actually needs — links, images, placeholders — rather than a general-purpose editor bolted in.
State and dataZustand for client state, TanStack Query for the server cacheSame pattern family as the portal. Every call routes through one fetch wrapper that includes the shared cookie and redirects to the EMR's own login on a 401.
APIThe same FastAPI backend the portal uses — over 160 routers, async throughoutThere is no EMR-specific service tier. The chart calls the canonical patient, clinical, vitals and scheduling routers directly.
DatabaseMySQL 8.0, SQLAlchemy 2.0 async, 391 Alembic migrations applied on startupIncluding purpose-built idempotent repair migrations for collation drift — tables created without an explicit collation break cross-table joins, and those migrations check before they alter.
Speech to textA three-tier engine: hosted Whisper, a self-hosted whisper-large-v3 container, or a deterministic fixture engineThe scribe is testable in an environment with no STT configured at all, and a clinic that will not send audio to a hosted service can run the container.
Draft generationAnthropic Messages API, model configurable per deploymentReceives the transcribed dialogue plus a structured patient context object — never raw audio, never the raw chart.
DocumentsJinja2 templates rendered by WeasyPrint, plus ReportLab generators for older document typesWeasyPrint is imported lazily so template rendering can be unit-tested without the native graphics stack. Clinic identity and legal text come from account settings, so documents are entity-branded.
e-PrescribingA vendor protocol with two implementations — a REST vendor and a GraphQL vendor — selected per entityRouters never branch on vendor. Credentials are encrypted at rest with Fernet and decrypted only inside the gateway; an unconfigured or unusable credential surfaces as a 503, not a crash.
TelehealthAn abstract vendor interface with a swappable adapter, selected per deploymentThe router never branches on vendor, so the video provider is a deployment decision rather than a code change — and the whole feature stays testable without depending on a live third-party account.
Interoperability exportA hand-written FHIR R4 NDJSON serializer shaped to the HL7 Bulk Data implementation guideIt advertises only the resource types it can actually serialize, so it never hands back a URL it cannot serve.
Architecture

How the engine actually works

Each section describes a mechanism that exists in the codebase today, not a pattern we admire.

The bet

There is no EMR patient table

The EMR does not maintain its own patient database. It reads and writes the same patients, encounters, problem, medication and allergy tables the network portal uses, scoped by the same entity mixin. There is no synchronisation job, no FHIR bridge between the two, and no mapping table translating an EMR patient id into a network patient id — because there is only one row.

Authorization

Two gates, and 404 on both

Two authorization strategies coexist deliberately. List endpoints — patient search, my panel — filter by the caller's single active entity from the token. Chart and detail endpoints do something different: they load the patient first, then authorize against the caller's entire grant set, including the entity ids each grant reaches.

The consequential action

Signing a note, and everything that follows

Signing is the most consequential thing a clinician does in the system, so it is implemented as an orchestrated, idempotent fan-out rather than a status flip. The route first runs a sign-authority gate: only the note's author or the encounter's designated attending may sign, and a null attending means author-only. It then completes the encounter, advances any linked appointment from scheduled or confirmed to completed — deliberately not touching one a human already marked cancelled or no-show — sets a co-sign requirement when a non-attending clinician signed, and writes an audit event.

The chart

Eighteen cards on one page, not eighteen sub-pages

The patient chart and the panel detail page share one layout contract: a sticky patient header, a sticky horizontally scrollable tab bar, and a vertical stack of full-width card sections — visit info, problems, medications, allergies, insurance, procedures, family history, immunizations, labs, imaging, referrals, prescriptions, vitals, care programs, prevention, care gaps, encounters and billing history, plus a conditional section for patients enrolled in a PACE program.

Orders and the network

The referral that authorises itself

When a clinician places a referral from the chart, a hook evaluates the IPA's delegation contract with the patient's health plan and the configured auto-approval rules, and returns one of five outcomes: auto-approved, which mints a utilization review and an authorization number atomically; pending review, which opens the case for human triage; faxed to payer, when the delegation does not cover that specialty; not required; or failed-retry.

Labs

One row that is both the order and the result

A lab order and its result are the same row, transitioning in place from pending to resulted rather than living in two tables that have to be joined and kept consistent. On ingest, the abnormal flag is computed, reflex rules are evaluated against configurable triggers to auto-create follow-on orders linked back to their parent, and a critical result opens a callback obligation with its own deadline.

Prescribing

Four safety checks, each independently fail-open

Before a prescription is signed, a consolidated panel runs four checks: drug-drug interaction against database-backed rules with per-IPA override rows, allergy match, duplicate therapy, and drug-disease contraindication. Each check fails open independently, so one check's failure does not suppress the other three. The rules moved out of hardcoded Python dictionaries into tables precisely so a clinical team can override them without a deploy.

AI

An ambient scribe whose guardrail is structural, not a disclaimer

The pipeline runs: the browser records audio chunks, each chunk is stored through a storage abstraction, an STT engine transcribes it, a diarization step attributes segments to provider, patient or other, and on stop the assembled dialogue plus a structured patient context object goes to the model to produce a draft note — which is then enriched with risk categories for any diagnosis in the assessment.

Billing

A code suggester that is deliberately not AI

Charge capture lets a clinician add billing lines to a signed encounter and build a claim from the encounter, its lines, the active problem list's diagnosis codes and the patient's demographics. A one-page superbill renders as a PDF.

Audit

The access log audits the act of reading the access log

The EMR's audit helper writes structural-only rows inside the same transaction as the mutation they record, so a rolled-back write cannot leave an orphaned audit entry — and it strips PHI-bearing values from the metadata blob before persisting: names, dates of birth, identifiers, free-text note fields, prescription directions.

Data model

The records underneath, and the rules that hold them true

Core entities and their invariants
EntityWhat it holdsRule that always holds
patientsThe demographic record — the same row the network portal reads, with medical record number, an entity-scoped human-readable id, merge pointer, and separate home and mailing addresses.Never hard-deleted; removal is a timestamp. Display id is unique per entity, not globally.
patient_care_teamWhich clinicians are on a patient's care team.This table is the panel gate — by-id chart routes consult it before anything else unless the caller holds a cross-panel permission.
patient_eventsAppend-only per-patient timeline carrying event type, source phase, source table and payload.No update, no delete, no soft delete. It is a record, not a working table.
clinical_encountersOne row per visit, with the attending provider that governs sign authority and explicit cancellation and no-show stamps.No delete column — the encounter moves through statuses and stays.
sign_event_outboxDurable at-least-once delivery for the side effects that fire when a note is signed.Unique on encounter and side-effect name — this is the idempotency mechanism for re-signs and retries.
patient_labsOrder and result on one timeline, with the observation code, numeric value, abnormal flag and a parent link for reflex-generated follow-ups.Status transitions only; there is no delete.
lab_standing_ordersRecurring order schedule with a next-due date.Unique on patient, test code and due date — the collision guard that makes the generating cron idempotent.
rx_ordersPrescription lifecycle, including which vendor transmitted it and the resulting vendor identifier.The transmitted-via value pins later cancel and refill calls to the vendor that originally handled it, even after an entity switches vendors.
controlled_rx_logImmutable controlled-substance audit trail with the schedule and a snapshot of the prescriber's registration.One row per prescription, append-only, no updated timestamp. A missing registration is written as null rather than omitted.
ai_scribe_sessions · ai_scribe_consent · ai_scribe_audio_chunksRecording session state, the consent captured before recording, and pointers to stored audio.Consent exists before any chunk is processed; audio has a retention deadline; chunks are unique per session and sequence.
AI scribe decisionsOne row per AI suggestion the clinician acted on, with original value, final value and the verb.Nothing reaches the chart without one of these rows — the human-in-the-loop guarantee is a constraint, not a policy.
referrals · referral_audit_eventThe referral order and one append-only row per authorization-hook evaluation with its outcome and reasoning.The referral is created regardless of hook outcome — the hook can never block the clinical action.
imaging_interface_logA record of the outbound order payload built for an imaging facility.Append-only. Every outbound payload is recorded as constructed, so what was sent to a facility can be reconstructed exactly rather than inferred from the order.
documents · document_versionsPatient documents with version history; after-visit summaries generated at sign time land here.A document points at its current version; versions are added, not replaced.
code_catalog_* tablesLocal caches for diagnosis, clinical terminology, observation, immunization and taxonomy code sets.Cache-first read, upsert on miss — with no external key configured the lookup serves from cache rather than failing.
Interfaces

What it exchanges, and in which direction

Optional integrations degrade gracefully: with no key configured the product still runs, it just does less.

e-Prescribing vendors

Bidirectional

Two vendor clients behind one protocol — one REST with per-request credentials, one GraphQL with OAuth2 and a Redis-cached token that evicts and retries once on a 401. Credentials are Fernet-encrypted at rest. No vendor configured means a clean 503.

Refill request webhooks

Inbound

Inbound pharmacy refill requests are deduplicated by a unique constraint on the vendor's request id, so a duplicate delivery is a no-op rather than a double-processed refill.

RxNorm (NLM RxNav)

Inbound

Proxied live per search, deliberately uncached. The backend route exists mainly to work around the upstream service's cross-origin policy.

NPI Registry (NPPES)

Inbound

Public federal registry treated as authoritative real-time data — not cached, not entity-scoped.

ICD-11 (WHO) and SNOMED CT (UMLS)

Inbound

Cache-first against local catalog tables with upsert on miss. Without a key the lookup runs cache-only rather than erroring.

Speech-to-text engines

Outbound

A three-tier chain — hosted, self-hosted container, or a deterministic fixture engine — so the scribe degrades to something testable rather than to an error.

FHIR R4 bulk export

Outbound

Asynchronous population export as newline-delimited JSON, shaped to the HL7 Bulk Data guide. It advertises only the four resource types it actually serializes.

Patient mobile and account API

Bidirectional

A separate patient-facing surface with its own token type, writing into the same vitals, medication, appointment and care-plan tables the chart reads. A reading submitted from a phone lands in the row a clinician sees.

AWS S3

Bidirectional

Audio, generated PDFs, chart documents and exports under entity-prefixed keys behind one-hour presigned URLs. Absent credentials, the service logs and skips.

Security

How access is decided and recorded

Shared identity, separate surfaces
The EMR and the network portal share one RS256 cookie and one auth router. The patient portal is a separate token type that the staff dependency rejects outright, so the two can never cross-authenticate.
Entity gate
Chart routes authorize against the caller's whole grant set rather than their single active entity, so legitimate oversight works without context switching. Denials are 404.
Panel gate
A second, narrower check requires care-team membership for by-id chart access unless a cross-panel permission is held — closing a URL-guessing hole the code documents explicitly.
Sign authority
Only the note's author or the encounter's attending may sign. A non-attending signature sets a co-signature requirement that drives a real queue.
Tenancy gate
The EMR front end blocks non-clinical entity types and the platform sentinel account from loading the clinic shell at all, rather than showing a working-looking but empty application.
Audit without PHI
Audit rows are written in the same transaction as the mutation and strip PHI-bearing values from metadata before persisting — names, dates of birth, identifiers, note text, prescription directions.
Accounting of disclosures
A per-patient access log implements the privacy-rule right to know who accessed a record, streams as CSV, and audits the act of viewing it as its own action.
AI and PHI
Raw audio reaches only the configured speech engine. The drafting model receives transcribed text and a structured context object. Audio is purged on a retention timer; transcript and draft are kept as the record.
Adolescent proxy masking
Results in sensitive categories for patients aged twelve to seventeen are withheld from a parent or guardian proxy account before release.
Compliance posture
Designed for HIPAA obligations under a BAA. An internal capability self-assessment against the federal certification criteria exists as an engineering artifact — it is a self-assessment, not a certification, and no certification is claimed.

Infrastructure is designed for HIPAA obligations under a BAA. We document the controls and hand over the runbooks; we do not claim a certification that does not exist for software.

Reliability

What keeps it correct under load

Outbox with a unique constraint
Sign-time side effects are keyed on encounter and side-effect name, so re-signs and cron retries cannot duplicate a document, a claim or a score recomputation.
Savepoint isolation
Each side effect runs in its own nested transaction — one failure rolls back itself, not the signature.
Fail-open by design where it matters
The authorization hook, the quality-measure credit and each drug-safety check fail open independently. A supporting service being down degrades a secondary outcome, never the clinical action.
Idempotent scheduled work
Standing orders, controlled-substance logging and refill webhooks are each guarded by a unique constraint rather than by a check-then-write, so concurrency and re-runs are safe.
Scheduler discipline
Around a dozen background jobs run single-instance with coalescing and a misfire grace window, so a slow run cannot stack up behind itself.
Bounded retries
The sign-event retry cron stops at three attempts; the vendor token retry evicts and retries exactly once. Nothing retries forever.
Idempotent repair migrations
Collation-drift migrations inspect the schema before altering and no-op when already correct, so they are safe to re-run across environments.
Stale-session cleanup
A scribe session left recording past a staleness window is auto-closed when the same clinician starts a new one, so orphaned in-progress rows do not accumulate.
Development record

The documents this was built from

Analysis, design discussion, implementation notes and QA written while the work happened. These are engineering artifacts in the product repository, not published pages — listed here so you can see what exists and ask for any of it.

Development-time documentation for EMR Solution
DocumentKindDateWhat it covers
EMR v3.0 milestone plan.planning/milestones/v3.0-EMR-FULL.mdPlan2026-05-27The canonical multi-phase build plan — module architecture, the design lock, the seed-data mandate and a coverage matrix across the clinical domains.
EMR functional parity matrix.planning/milestones/v3.0-EMR-FUNCTIONAL-MATRIX.mdAnalysis2026-05-27Per-domain function, business-logic and edge-case matrix mapped to build phases, including the shell.
EMR v3 implementation architecturedocs/emr-v3-architecture.htmlDesign2026-05-28Implementation architecture and competitive map for the v3 build.
Access model — options and decisiondocs/emr-access-model.htmlDesign2026-05-23A bilingual walkthrough of the access model as it then stood and four incremental tightening options. Three of the four — panel restriction, attending sign lock and co-signature — are implemented in the code today.
Access model — follow-on issuesdocs/emr-access-bcd-issues.htmlAnalysis2026-05-23The issues anticipated once the tightening options were adopted, written before they were.
Access model discussion logdocs/emr-access-discussion-log.htmlDiscussion2026-05-23The running discussion that produced the sequencing of the access-model changes.
Phase specs and user guides — labs, eRx, imaging, referrals, billing, scribe, telehealth, portal, reports.planning/phases/P02..P10/{SPEC,USER_GUIDE,SELF_CHECK,SEED-NOTES}.mdDesign2026-05-27 to 2026-05-28Each ancillary module carries a design spec, an end-user guide surfaced in the in-app guide drawer, an implementation self-check and notes on the seed data shipped with it.
Inbox and shell phase.planning/phases/P01-inbox/ · P01.5-inbox-v2/ · P01.6-chart-orders/Design2026-05-27The shell and inbox build, its second iteration, and the phase that moved order placement onto the chart page itself.
Certification criteria self-assessment.planning/phases/P10-reports/ONC_CAPABILITY_MAP.mdAnalysis2026-05-28A criterion-by-criterion internal assessment marking each as ready, partial, gap or out of scope, with evidence paths. An engineering self-assessment intended to scope a future testing engagement — not a certification, and not presented as one.
Legacy EMR data-migration playbookdocs/emr-data-migration.htmlMigration2026-08-21The ETL approach for moving off a legacy system — extract, transform and load across patients, encounters, problems, medications, orders, results, documents and charge history, using the built-in terminology catalogs and an external-id reconciliation model.
New clinic install and configuration guidedocs/emr-clinic-setup-guide.htmlRunbookThe operational runbook for standing up a new clinic on the platform.
Per-module documentation setdocs/docs-module-emr-*.htmlReference2026-06-04 to 2026-06-05Written documentation for each module — inbox, labs, prescribing, imaging, referrals, billing, scribe, telehealth, portal, reports and settings.
External API catalog and medical code mapdocs/docs-external-apis.html · docs/medical-codes-map.htmlReference2026-06-04 to 2026-06-05Every external service the platform integrates, and the coding systems used across it.
Migration hygiene, QA verification and security-performance reportsdocs/emr_db-migration-hygiene-report-*.html · emr_qa-verification-report-*.html · emr_security-perf-report-*.htmlQA2026-07-15 to 2026-07-16A migration hygiene audit, a QA verification pass, and a combined security and performance review.
Daily build reportsdocs/emr_daily-report-*.htmlReport2026-07-16 to 2026-07-20Day-by-day progress records written during the active build period.
Credentialing migration rescue report.planning/qa-reports/2026-05-22-emr-credentialing-migration-rescue/REPORT.mdQA2026-05-22An incident record for a data migration that needed rescuing, and what was changed as a result.
Glossary

Terms used on this page

Encounter
One clinical visit — the container a note, orders, vitals and billing lines attach to.
Sign event
The moment a note becomes final, and the fan-out of side effects that follows it.
Outbox
A table recording work that must happen at least once, so retries are safe and duplicates are impossible.
Savepoint
A nested transaction boundary, so one failing step rolls back alone.
Panel
A clinician's assigned patient roster, defined by care-team membership — narrower than entity access.
Attending lock
The rule that only a note's author or the encounter's attending may sign it.
Co-signature
The counter-signature required when a non-attending clinician signs, driving its own queue.
After-visit summary
The patient-facing document generated automatically at sign time.
eRx
Electronic prescribing generally, as distinct from the controlled-substance subset.
EPCS
Electronic prescribing of controlled substances — a federally regulated, identity-proofed workflow.
Reflex rule
A rule that automatically orders a follow-on test when a result meets a trigger condition.
Standing order
A recurring order generated on a schedule rather than placed each time.
LOINC
The standard vocabulary identifying laboratory and clinical observations.
RxNorm
The normalized drug naming vocabulary used to identify medications unambiguously.
HL7 v2 / ORU / ORM
The long-established clinical messaging standard, and its result and order message types.
FHIR bulk export
An asynchronous population-level export of records as newline-delimited JSON.
Diarization
Attributing segments of a transcript to distinct speakers.
Accounting of disclosures
A patient's right to be told who has accessed their record, implemented here as a per-patient access log.
Collation drift
A MySQL failure mode where tables created with differing default collations break cross-table comparisons.
BAA
Business associate agreement — required before protected health information may be shared with a vendor.
Questions

Asked by the people who evaluate this

Is 'one shared record' just marketing for a well-behaved integration?

No, and the difference is checkable. There is no EMR patient table, no identity crosswalk and no reconciliation job in this repository. Both applications call the same routers against the same rows. An integration, however well-behaved, has two copies and a policy about which one wins; this has one copy and no policy to get wrong.

How is the AI scribe kept from writing into the chart on its own?

Structurally. Every suggestion becomes a decision row with the original value, the final value and whether the clinician accepted, modified or rejected it. Nothing commits without one. That means 'what did the model propose versus what was signed' is a query anyone can run, which is a stronger guarantee than a policy stating the same thing.

Do you send patient audio to a model provider?

No. Raw audio goes only to the configured speech-to-text engine, which can be a self-hosted container if a clinic will not send audio off-premises at all. The drafting model receives transcribed text and a structured context object. Audio is purged on a retention timer; the transcript and draft are kept as the clinical record.

Do you hold a federal health-IT certification?

No, and we will not imply otherwise. There is an internal capability self-assessment in the repository that walks the criteria and marks each ready, partial, gap or out of scope with evidence paths — written by engineers to scope a future testing engagement. It is a planning artifact. Anyone presenting a self-assessment as a certification is misrepresenting it.

What happens when an authorization service is unavailable mid-visit?

The referral is created anyway. The hook returns a retry outcome, a cron picks it up later, and the clinician is not blocked. That is a deliberate inversion of the usual failure mode, where an administrative service being down stops clinical work.