On this page — 25 sections
Detailed system documentation
The complete reference for PHI AI — the AI-native platform for protected health data — in one place: what it is, every profile and permission, every screen, every record type in the Data Store, how the assistant works, how the audit trail works, the full architecture with its data flows, the animated flow diagrams, and every programmatic surface. Written to be read by anyone who works in or around the platform, not only engineers. Sections follow the role signed in: parts a role cannot use are not shown.
Part I — The system: purpose · design principles · identity, roles & permissions · purpose of use · screen-by-screen reference · the Data Store · the assistant in depth · the audit trail · security model · extensibility & model integration · configuration
Part II — Full architecture with data flows: the animated picture · component reference · trust boundaries · cloud architecture · failure modes
Part III — Data flow diagrams: the six journeys, animated and walked through step by step
Part IV — API details: shared conventions · retrieval tools · the model API · route surface · ingestion · export · vendor authentication · error model
1. Purpose and positioning
PHI AI is an AI-native platform for protected health data — designed from first principles for AI as a first-class, governed consumer of the clinical, financial and behavioral-health record. It is not an AI feature bolted onto a records system: the architecture exists because AI reads this data. The role-scoped retrieval tools, the sensitivity segmentation, the purpose-of-use tagging, the hash-chained audit of every model interaction, the invariants and the signature gates are not add-ons around a database — they are the platform, built for the moment a model touches a protected record. EMRs connect to it as data planes: sources records arrive from, targets signed work delivers to.
It does three jobs:
- It ingests. Clinical, financial and behavioral-health records arrive from source EMRs — in bulk (whole patient populations over the FHIR Bulk Data API) and in real time (admissions, discharges, lab results and payment remittance on streaming feeds) — and are encrypted, classified for sensitivity, and indexed into the Data Store, where they become AI-ready: every record carries the classification and provenance the gates need before a model may read it.
- It answers and drafts. A retrieval-tuned AI assistant (PHI RAG) answers questions from the Data Store with citations, drafts clinical documentation, triages inboxes, abstracts quality measures, and screens cohorts — always inside the asker's role, always on the record.
- It governs. Consent gates, sensitivity segmentation, purpose-of-use tracking, signature requirements, export controls and an append-only audit trail make every use of the data visible and every refusal a recorded event — governance designed for AI use of PHI, not retrofitted to it.
Equally important is what the platform is not:
- It is not a bolt-on. Nothing here was retrofitted onto an existing product: the data model, the gates and the audit chain were designed together, for AI use of protected data, from the first line. Signed clinical documentation still delivers to the EMR, which remains the chart of legal record — a legal reality, not an architectural dependency.
- It is not autonomous. No generated text enters a chart, and no record leaves the organization, without a human decision. Drafts wait in the signature queue; exports pass consent gates.
- It is not a medical device. It issues no diagnostic or treatment directive; every answer's basis is independently reviewable through its citations (see Compliance & responsibility).
2. Design principles
Two principles sit above everything else; five more run through every screen and every API in their service.
- AI for PHI. The platform exists to put AI at the center of protected health data — not beside it, not bolted on after. Every design decision below is downstream of this one: the AI is a first-class, governed consumer of the record, reaching data through the same gates as people, citing what it reads, and drafting rather than deciding.
- Compliance first. Compliance is the core design principle, not a policy layer: the rules live in the shape of the system. Population tools return counts and cannot enumerate; unsigned drafts have no write path; psychotherapy content has no export path; recording waits for the jurisdiction's consent. Where a rule can be structural, it is.
- Minimum necessary. Every profile holds an enumerated list of permissions. A screen, record or tool outside that list does not exist for that profile — the navigation does not show it, the route refuses it, and the assistant does not hold the tool. Nothing is reachable "because the database has it."
- Nothing silent. Every question asked of the AI, every record it reads, every export, every configuration change, every override and every refusal is appended to a hash-chained audit trail that cannot be quietly edited. When records are withheld from an answer or an export, the withholding is counted and stated, never invisible.
- Fail closed, refuse loudly. A gate that cannot decide denies. A run that cannot finish is marked failed, never partially-complete. And a refusal is presented as a first-class outcome — in run histories, on screens, in the audit trail — because a gate that fails closed is the product, not an error condition.
- A human signs. AI-drafted documentation is a draft until a licensed person signs it (the signature rule). Population queries return counts, never named lists, on the de-identified plane (the counts-never-lists rule).
- Calm surfaces. The interface is designed for people who live in an EMR all day: one identity card, collapsible detail, no autoplaying anything, and a design language where color always means something — orange marks state that demands attention, green marks records arriving, navy marks reading.
3. Identity: profiles, roles and permissions
Access control is two-layered: a profile (a person) holds one or more roles, and each role grants an enumerated set of permissions. Screens and actions check permissions, never roles, so a new role can be composed without touching a single screen.
3.0 Where roles come from — and where they do not
This system is not the source of truth for users or roles, and is not intended to be. It is designed to be governed by your organization's identity source of truth — your identity provider and directory (Entra ID / Okta / LDAP or equivalent), where accounts are created, roles are assigned, access is reviewed, and departures are processed. The platform's job is enforcement, not authority:
- Identity arrives established. In the recommended deployment the platform trusts an identity your reverse proxy / SSO has already authenticated, or one a SMART-on-FHIR launch carries from the EMR. The platform authenticates nobody in that mode and stores no credential.
- Role assignments originate upstream. The roles an identity carries are asserted by your identity layer; the platform maps them to its enumerated permissions and enforces them on every route, tool and option set. Change a person's role in your directory and the platform's enforcement follows — there is no separate role store to drift out of sync.
- Local accounts are a fallback, not the design. The optional local-accounts mode exists for organizations with no identity provider at all; it is off by default, and it is the only configuration in which this platform holds a credential. If you have an IdP, use it.
- The Control panel's matrix is an enforcement view, not a user directory. It shows what each role can reach so reviews and audits have a live artifact — it is not where people are provisioned, and deliberately has no "create user" button in IdP-governed deployments.
- Reviews and deprovisioning belong to your source of truth. Access recertification, joiner/mover/leaver processing and emergency revocation all happen in your identity system; the platform reflects the result on the next request. (See Compliance & responsibility — access governance is an organizational duty this software cannot discharge.)
3.1 Profile archetypes
Profiles are composed from roles; the identities behind them come from your identity provider (§3.0). The archetypes below are the working realities the role set is designed around — this demonstration site's persona switcher signs you in as one archetype at a time. It also offers a second group that holds one role each, so every role can be inspected on its own: a composite can never show you a boundary, because its other roles keep filling the gap. Signing in as the auditor alone is how you see that an auditor is not a clinician with extras.The demonstration cast itself is documented with the rest of the synthetic environment in Emulators & non-PHI setup.
| Archetype | Roles | Working reality this models |
|---|---|---|
| Treating clinician | Clinician | Charts in context, the assistant, drafting and signing. No claims detail, no audit access, no psychotherapy notes — treatment does not need them. |
| Records professional | Health Information Management, Auditor | Release of information, claims, integration monitors, and read access to the audit trail. The person a privacy officer calls first. |
| Analyst | Analyst | Population health and research: cohorts, risk models, quality measures — on the de-identified plane, where no record-level identifier ever renders. |
| Researcher | Researcher | Record-level work across many charts under an IRB or privacy-board approval, with the research purpose stamped on every read. Distinct from the analyst, who counts a population and cannot open a chart, and from the clinician, who opens the chart of the patient in front of them. |
| Behavioral-health clinician | Clinician, Psychotherapy | Ordinary chart access plus the psychotherapy store. The only archetype that can open a psychotherapy note. |
| Operations administrator | Administrator, Auditor, Disposition | EMR connections, import/export managers, configuration, retention decisions, audit review. Cannot open a chart — operations does not need one. |
| System administrator | System Administrator (wildcard) | Every permission, plus the Control panel no other archetype sees. Full control is not exemption: every switch is audited under the operator's name. |
3.2 The permission matrix
Twelve permissions exist. The wildcard * (System Administrator only) grants all of them.
| Permission | Grants | Clinician | HIM | Auditor | Analyst | Psychotherapy | Administrator | Disposition |
|---|---|---|---|---|---|---|---|---|
| patient:search | Find patients by name or MRN | ✓ | ✓ | — | — | — | — | — |
| patient:read | Open charts, labs, documents | ✓ | ✓ | — | — | — | — | — |
| assistant:use | Ask the AI assistant | ✓ | ✓ | — | ✓ | — | ✓ | — |
| claims:read | Claims, remittance, prior auth | — | ✓ | — | — | — | ✓ | — |
| roi:manage | Release-of-information queue | — | ✓ | — | — | — | — | — |
| analytics:query | Cohorts & population screens | — | ✓ | — | ✓ | — | ✓ | — |
| psychotherapy:read | The psychotherapy store | — | — | — | — | ✓ | — | — |
| audit:read | Read the audit trail | — | — | ✓ | — | — | — | — |
| integration:view | Import/export managers | — | ✓ | — | — | — | ✓ | — |
| admin:config | EMR configuration, run kickoffs | — | — | — | — | — | ✓ | — |
| retention:read | Retention & disposition | — | — | — | — | — | — | ✓ |
| system:admin | The Control panel | — | — | — | — | — | — | — |
| sensitivity:view | Sensitive-categories operations screen | — | ✓ | — | — | — | ✓ | — |
| report:read | Data Store inventory & integrity checks | — | ✓ | ✓ | — | — | ✓ | — |
| integration:export | Export managers, EMR-connection docs | — | ✓ | — | — | — | ✓ | — |
| ingest:write | Import an individual patient from the source EMR | ✓ | ✓ | — | — | — | ✓ | — |
| ambient:capture | Consent-gated ambient documentation capture | ✓ | — | — | — | ✓ | — | — |
Three deliberate asymmetries are worth noticing, because they carry the security model:
- Psychotherapy is not a superset. Holding psychotherapy:read grants nothing else. The behavioral-health clinician profile holds Clinician and Psychotherapy; a hypothetical account holding only Psychotherapy could open a psychotherapy note and nothing besides.
- Auditor is read-only and narrow. The Auditor role holds exactly audit:read. Audit review does not require chart access, so it does not include it.
- The Administrator cannot open a chart. Operations work — connections, imports, configuration — never requires reading a patient's record, so the Administrator role does not include patient:read. When an Administrator hits a chart URL, the refusal lands in the audit trail like any other.
3.3 Refusal behavior
A request outside the profile's permissions returns HTTP 403 with a page that names the missing permission, and writes an access.denied event carrying the actor and the permission that was missing. Refusals are not exceptions to the audit trail — they are among its most useful entries, because a pattern of denials is how a privacy officer discovers someone probing beyond their role.
4. Purpose of use
Every action that touches a record carries a purpose of use, integrated into the workflow where the action happens. The five purposes mirror HIPAA's permitted-use structure:
| Purpose | Meaning | Typical actor |
|---|---|---|
| Treatment | Care of the individual patient | Clinician, psychotherapist |
| Payment | Billing, claims, remittance, prior authorization | HIM, revenue cycle |
| Health care operations | Quality, training, administration, integration | Administrator, analyst |
| Patient request | The patient asked — access, amendment, ROI | HIM |
| Legal | Subpoena, court order, counsel review | HIM, compliance |
The purpose is not decorative, and it is deliberately not a session mode — there is no ambient purpose selector, because a purpose of use is an assertion about a specific action, not a state a session sits in. It is asserted where data actually moves: the assistant collects it per question, an ROI request records its purpose at creation and every resolution of that request carries it, signature commits are treatment by definition, operations actions are operations — and where no explicit choice is called for, the default derives from the role doing the work (a clinician's reads assert treatment; records and operations roles assert operations). Every audit event stores the result, which is what lets the organization answer the two questions HIPAA's accounting and minimum-necessary rules keep asking: who touched this record, and under what claimed justification. In the deployable platform the purpose additionally travels as an IAM session tag, so even the cloud permission layer knows why a read is happening.
5. Screen-by-screen reference
5.0 Sticky patient context — the one-patient-per-screen contract
Wrong-chart errors happen when a screen shows one patient while the clinician believes they are working on another. The platform prevents this with a contract enforced by screen class, not by convention:
- Single-patient screens (chart, claim, imaging study, draft review, release review, prior authorization) synchronize the patient-context strip to the patient they render — the strip can never name a different person than the page below it. Each context move is an audited session.context event.
- Patient-linked worklists (signature queue, inbox, imaging worklist, claims queues, release requests) show only the in-context patient's items, behind one shared scope banner. Widening to all patients takes an explicit action and is recorded on the audit trail as session.scope_widened.
- Documents file to their own patient. An ambient visit note attaches to the visit's patient — never to whoever happens to be in context, and never to a stand-in. If that patient cannot be resolved, the platform refuses to draft rather than file to the wrong chart. The ambient encounter list itself is scoped like every other worklist, and opening an encounter moves the context to the visit's patient before anything renders.
Every screen is classified against this contract at build time, a runtime tripwire records any worklist that renders outside it, and the regression suite derives its coverage from the same classification — so a new screen cannot ship unclassified.
5.1 Clinical workspace
| Screen | Requires | What it does |
|---|---|---|
| PHI AI assistant | assistant:use | The platform's front door: the PHI RAG prompt interface with the running thread, prompt history and saved prompts. Every question is audited; answers carry citations and withholding counts. |
| Patients & charts | patient:search | Search by name or MRN across the population; paginated roster with demographics and payer. |
| Patient chart | patient:read | The storyboard: demographics banner, problems, medications, allergies, encounters, labs with abnormal flags, documents, imaging, claims summary — with a callout stating how many sensitive records were withheld from this view and why. The psychotherapy section renders only for the Psychotherapy role. |
| Summarization | patient:read | Chart-in-context summarization: what the assistant produces, what it cites, what it refuses to infer. |
| Inbox triage | patient:read | Results and messages ranked by clinical urgency, each ranking traceable to the record that caused it. |
| Ambient documentation | patient:read | The functional ambient pipeline: consent gate (jurisdiction- and modality-aware) → self-hosted ambient listener (registry slot ambient) → speaker-labeled transcript → SOAP draft into the signature queue. Synthetic encounters on this site; no live microphone by design. |
| Patient instructions | patient:read | Discharge and after-visit instructions drafted at a patient-appropriate reading level, from the visit's actual records. |
| Signature queue | patient:read | the signature rule's home: AI drafts wait here for a licensed reviewer to edit and sign. Signing writes signature.committed; nothing advances by default or timeout. |
5.2 Revenue & records
| Screen | Requires | What it does |
|---|---|---|
| Claims & billing | claims:read | The financial plane: claims with line items, payers, adjudication status, denials and totals across the population. |
| Claim detail | claims:read | A single claim: service lines, CPT codes, billed/allowed/paid amounts, adjudication history. |
| Prior auth & appeals | claims:read | Payer criteria checked against real chart evidence. Each criterion shows the record that satisfies it — or shows the gap. Nothing is inferred to fill a gap. |
| Documentation gaps | claims:read | Coding-support review: where documentation does not support the coded level, shown as evidence, drafted queries awaiting human sign-off. |
| Release of information | roi:manage | The ROI queue: requests with requester, scope and legal basis. Fulfilling or denying writes roi.fulfilled / roi.denied. Requests touching Part 2 content without Part 2 consent cannot be fulfilled. |
| Sensitive categories | — | The segmentation explainer: which categories exist, how records are classified, what each category's handling rule is. Readable by everyone, because the rules are not secrets. |
5.3 Population
| Screen | Requires | What it does |
|---|---|---|
| Cohort builder | analytics:query | Condition-based cohort counts on the de-identified plane. Returns numbers, never lists (the counts-never-lists rule); each query writes analytics.query. |
| No-show risk | analytics:query | Appointment no-show modeling: features, calibration, and the fairness constraints the model must satisfy before use. |
| Scheduling optimization | analytics:query | Template and capacity optimization under fairness constraints — overbooking burdens must not concentrate on protected groups. |
| Trial pre-screening | analytics:query | Protocol criteria matched against de-identified records; candidate counts go to study staff, identities do not. |
| Quality measures | claims:read | eCQM abstraction: numerators and denominators computed from the record, each element traceable to its source. |
| Ingest & mapping QA | — | Terminology mapping quality: unmapped codes, mapping confidence, and the review queue for low-confidence matches. |
| Imaging worklist & AI read | patient:read | Imaging studies with modality, body site and report status; the study viewer's AI imaging read is a genuine vision inference over the frames — the registry's imaging-slot model describes what is visible, measures discrete findings from the acquisition scale, and drafts a structured report into the signature queue. On this site the frames are synthetic phantoms and the read says so. |
| Data Store | — | The live holdings audit: every table with row counts and date ranges, plus six integrity checks run on load (see §6.4). |
5.4 Integration
| Screen | Requires | What it does |
|---|---|---|
| Source & target EMRs | admin:config | Connection configuration for the source EMR (records arrive from) and target EMR (records deliver to), with each of the six supported vendors' real seams: auth method, bulk capability, write surface. |
| Bulk import manager | integration:view | FHIR Bulk Data runs: history with per-run status and record counts, the watermark rule, and a live kickoff honoring the vendor's 24-hour window — a second kickoff inside the window is refused and recorded. |
| Streaming data | integration:view | Inbound feeds (ADT, ORU labs, 835 remittance) with per-partition checkpoints. A gap between checkpoint and latest offset is surfaced loudly, never interpolated. |
| Bulk export manager | integration:view | Outbound bulk: deliveries to the target EMR, HIE contributions, registry submissions, ROI productions. Sensitive-category exclusions counted; Part 2 redisclosure refused without consent; partial exports marked failed. |
| Streaming export | integration:view | Outbound feeds under acknowledgment discipline: acknowledged vs produced sequence per feed; held means queued and accounted; paused means a governance decision that accumulates rather than leaks. |
5.5 System
| Screen | Requires | What it does |
|---|---|---|
| Control panel | system:admin | The System Administrator's seat: the model registry (every model of every kind — LLM, predictive, classifier, optimization, mapper — with bring-your-own registration by provider/model ID or HTTPS endpoint, and an audited registered → enabled → activated lifecycle per capability slot), per-model management panels for the assistant models, live controls (retrieval switch, model kill switch, bulk-hold release, feed pause/resume), a live audit-chain verification, the full user-and-permission matrix, the full editable platform configuration, and the recent administrative-action log. |
5.6 Governance
| Screen | Requires | What it does |
|---|---|---|
| Registry & preflight | — | Every AI capability is registered before use; preflight verdicts check a capability's declared scope against what it is about to do. |
| Fairness screen | — | Subgroup performance for the predictive models: where a model underperforms for a group, the screen shows it rather than averaging it away. |
| Ambient consent gate | — | Deny-by-default recording consent, keyed on jurisdiction and modality, with visit-level attestation. Every evaluation writes consent.evaluated. |
| Source attributes | — | Provenance: which source system, which run and which mapping produced each record class. |
| EMR conformance | — | Per-vendor conformance posture: what each EMR's API actually supports, verified rather than assumed. |
| Retention | retention:read | Retention schedules by record class and the disposition queue — what is due, what holds apply, who decides. |
| Audit | audit:read | The trail itself: filterable events with actor, action, object reference, purpose, and chain position. |
6. The Data Store
6.1 Record types
On this demonstration site the Data Store holds a fully synthetic population — no real person's data appears anywhere — with full longitudinal records. Every record class the platform can house is populated:
| Record class | Contents | Notes |
|---|---|---|
| Patients | Name, MRN, birth date, gender, address, phone, payer, deceased flag | Names are cleaned human names, not identifiers — the roster reads like a real one. |
| Encounters | Type, period, reason, provider, facility | Up to 25 most recent per patient. |
| Conditions | Diagnosis, onset/abatement, status, sensitivity class | Social-history noise (employment status, "medication review due") is filtered at ingestion. |
| Medications | Drug, status, authored date, sensitivity class | Deduplicated to the latest order per drug. |
| Allergies & immunizations | Substance/vaccine, dates, status | |
| Procedures | Procedure, date, encounter linkage | |
| Observations | Labs and vitals on a curated LOINC panel (A1c, lipids, renal function, CBC, blood pressure, BMI…) with abnormal flags | Reference-range logic marks abnormals (A1c ≥ 6.5, SBP ≥ 140, …). |
| Documents | Clinical notes with body text and sensitivity class | Notes classified sensitive are withheld from ordinary retrieval with the withholding stated. |
| Imaging studies | Modality, body site, report status | |
| Claims & claim lines | Payer, billed/allowed/paid, adjudication status, CPT-coded service lines | The financial plane — in scope by design, queryable by the assistant for claims:read holders. |
| Psychotherapy notes | Session notes, kept in a physically separate table | See §6.3. Never exported, never in ordinary retrieval. |
| ROI requests | Requester, scope, legal basis, status | |
| Signature drafts | AI drafts awaiting signature | |
| Prompts | Every question asked of the assistant, per user, with saved flags | Questions are stored; answers are not — an answer is regenerated from records, not cached as a shadow chart. |
| Integration state | Bulk runs, stream partitions, bulk exports, stream feeds, configuration | The operational record the integration screens render. |
| Audit log | The hash chain (see §8) | Append-only. |
6.2 Sensitivity classification
At ingestion, every condition, medication and document is classified into one of five sensitive categories — or none. Classification is first-match-wins against curated terminology:
| Category | Code | Covers | Handling rule |
|---|---|---|---|
| Substance use disorder | sud_part2 | SUD diagnoses and treatment: opioid/alcohol dependence, MAT medications (buprenorphine, methadone, naltrexone), overdose events | 42 CFR Part 2: withheld from ordinary retrieval; redisclosure in any export refused without Part 2-specific consent. |
| Mental health | mental_health | Psychiatric diagnoses (depression, anxiety, PTSD, bipolar) and psychotropic medications (SSRIs, benzodiazepines…) | Withheld from ordinary retrieval with counts stated; visible to treating roles where permitted. |
| Reproductive health | reproductive | Pregnancy, contraception, fertility care | Withheld from ordinary retrieval; state shield laws may impose stricter export rules. |
| HIV | hiv | HIV status and antiretroviral therapy | Withheld; many states require specific consent for disclosure. |
| Genetic | genetic | Genetic tests and screening results | Withheld; GINA constrains downstream use. |
Two honest limits of the demonstration classifier, stated because a production deployment must do better: it is keyword-driven (a production system uses curated value sets — SNOMED, RxNorm, LOINC — maintained clinically), and first-match-wins means a record in two categories carries its first. The handling pipeline — withhold, count, state, refuse redisclosure — is the part the demonstration reproduces faithfully.
6.3 The psychotherapy store
Psychotherapy notes are not a sensitivity class — they are a separate store. This mirrors HIPAA's own structure, which defines psychotherapy notes as notes "separated from the rest of the individual's medical record" (45 CFR §164.501). Concretely:
- They live in their own table, never joined into chart queries.
- Only psychotherapy:read can list or open them, and the assistant requires an explicit minimum-necessary confirmation before disclosing session content even to that role.
- Every listing writes psychotherapy.list; every read writes psychotherapy.disclosure.
- No export path includes them. Not bulk, not streaming, not ROI. The export managers count them among the withheld.
6.4 The Data Store audit page
The Data Store screen renders, live on every load: per-table row counts, earliest and latest record dates per class, and six integrity checks — orphaned-reference sweeps, encounter linkage, sensitivity-classification coverage, duplicate detection, the psychotherapy separation invariant, and a full audit-chain verification. A failing check renders as a failure; the page does not average problems away.
7. The assistant (PHI RAG) in depth
7.1 The retrieval contract
PHI RAG is a retrieval-tuned system: the language model never answers about records from memory. The contract, enforced by the system prompt and the tool design together:
- Answer only from retrieved records. Every factual claim about a patient cites its source in the form [MRN-###### · chart].
- Never assert absence. The model may not claim a record does not exist or a document does not say something unless it retrieved and read that document. "Not found in what I retrieved" is the honest formulation, and the required one.
- Confirm before sweeps. A question requiring a population-wide read triggers a minimum-necessary confirmation: the assistant states what it would have to read and asks before reading.
- State the withholding. When the gate withholds sensitive records from retrieval, the answer says how many and under which rule — the reader knows the answer is incomplete and why.
- Counts, never lists. On the population plane, the assistant returns cohort sizes, not names (the counts-never-lists rule). A request to enumerate patients with a condition is refused with the invariant named.
7.2 Threads, history and saved prompts
The assistant keeps a per-session thread (the last 12 turns render; the model sees the last 6). Every question is stored per-user in prompt history — questions only, never answers, so history is a workflow convenience rather than a shadow copy of PHI. Any question can be saved for reuse; clicking a history or saved item prefills the ask box and runs nothing until the user presses Ask.
7.3 Degradation ladder
The assistant degrades in defined steps, never silently: live model with retrieval → retrieval disabled (System Administrator switch: the assistant refuses to discuss records at all and says why) → live calls off or key absent (the deterministic scripted fallback answers, labeled as such) → model call failure (the error is stated; the question was already audited; nothing was read). At no rung does it guess.
8. The audit trail
8.1 Event anatomy
Every event is one appended row: at · actor · action · resource_ref · purpose · prev_hash · event_hash. The event_hash is the SHA-256 of the event's content plus the previous event's hash, which chains the log: editing or deleting any historical row changes every hash after it, and the Control panel's verification (which re-derives the entire chain on every load) turns from intact to BROKEN with the first bad row identified.
Object references, never content: the log records that a psychotherapy note was disclosed to its treating clinician for treatment — it does not copy the note. The audit trail must be reviewable by auditors without itself becoming a PHI disclosure surface.
8.2 Event catalog
| Event | Written when |
|---|---|
| ai.query | Any question is asked of the assistant (before any retrieval happens). |
| phi.search | A patient search runs (screen or assistant tool). |
| phi.disclosure | A chart, lab set, document or claims record is read — by a person or by the assistant on a person's behalf. |
| psychotherapy.list / psychotherapy.disclosure | Psychotherapy sessions are listed / a session note is read. |
| analytics.query | A population count runs on the de-identified plane. |
| access.denied | Any refusal: missing permission, missing consent, missing role. |
| consent.evaluated | The ambient consent gate renders a verdict. |
| consent.attested | A visit-level verbal attestation is captured, unlocking all-party-state recording. |
| ambient.capture_opened | An ambient capture session opens (with a model.inference event naming the listener). |
| ambient.transcribed | A capture's transcript is committed to its recording (with a model.inference event naming the engine that produced it). |
| signature.committed | A human signs a draft (the signature rule's event). |
| roi.fulfilled / roi.denied | A release-of-information request is resolved. |
| bulk.completed / bulk.refused | A bulk import run finishes / is refused by the rate-limit window. |
| export.completed / export.refused | A bulk export delivers / is refused by the consent gate or write surface. |
| config.changed | EMR connection configuration is saved. |
| system.* | Control panel actions: system.rag_on/off, system.assistant_on/off, system.feed_paused/resumed, system.hold_released. |
8.3 What the trail is for
Three consumers, three uses: the privacy officer answers a patient's accounting-of-disclosures request from phi.disclosure and roi.* events; the security team reads access.denied patterns and system.* overrides as its detection surface; and the auditor verifies the chain and samples events against source screens. In the deployable platform, concurrent writers can legitimately fork the chain (two processes appending at once); forks are recorded and merge verification handles them — a fork is not tampering, and verification knows the difference.
9. Configuration reference
Runtime configuration lives in the platform configuration store, editable only through audited screens:
| Key | Meaning | Set from |
|---|---|---|
| source_vendor / source_base_url / source_client_id / source_group_id | The source EMR connection: vendor, FHIR base, OAuth client, bulk-export group | Source & target EMRs |
| target_vendor / target_base_url / target_client_id | The delivery target for write-back and bulk export | Source & target EMRs |
| rag_enabled | PHI RAG retrieval switch — off means the assistant refuses to read the Data Store and says so | Control panel · PHI RAG |
| assistant_live | Live model calls — off means the scripted fallback answers, labeled as such | Control panel · the active assistant model |
10. Security model — the consolidated view
Each control above appears where it operates; this section is the one place they are stated together, layer by layer.
- At rest. Every stored object is envelope-encrypted before it lands: a per-object data key, wrapped by a organization-managed KMS key that never leaves the key service. Key use is logged by the cloud, so a decryption that bypassed the application would still leave evidence. The psychotherapy store is physically separate with its own access policy.
- In transit. TLS 1.2+ end to end — EMR↔platform, platform↔storage, platform↔KMS, platform↔model provider — and the storage buckets carry policies that refuse non-TLS requests rather than merely preferring encrypted ones.
- Identity & session. The application binds localhost and trusts only a reverse-proxy-established identity (your SSO), or SMART launch from the EMR, or — as the fallback — local accounts. Sessions are signed; every mutation requires the session's CSRF token; roles mirror cloud IAM roles so the application layer and the infrastructure layer cannot disagree about who may do what. The platform is not the source of truth for users and roles — your organization's identity system is.
- AI-specific controls. The model receives excerpts, never a database connection. Tool schemas are generated per role, so an unpermitted capability is absent rather than refused. Temperature 0, bounded output, a six-turn context tail — the model never accumulates unbounded PHI context. Model traffic runs inside your BAA'd cloud account (Bedrock, Vertex AI, Azure AI Foundry) or under a dedicated agreement with whichever vendor's API you call directly. None of these controls depend on which model answers — they are properties of the platform, so changing model vendors changes the answer's author and nothing about what it is allowed to see. Psychotherapy access for the assistant is a separate, default-off switch.
- Structural refusals. The controls above are not policies asking models or people to behave: population tools return counts and cannot enumerate; unsigned drafts have no write path; psychotherapy content has no export path. The refusal is in the shape of the API, not in a prompt.
- Monitoring. The hash-chained audit trail is re-verified on every Control panel load; model analytics watch drift and fire alerts that never auto-close; access.denied patterns surface probing. What the platform cannot see — your risk analysis, your BAAs, your workforce training — is enumerated in Compliance & responsibility.
11. Extensibility — frontier models, your models, your features
PHI AI is a highly configurable platform, not a fixed product. Three extension surfaces are part of the core design, and all three are managed — registered, enabled, activated, monitored and audited — through the same Control panel, never through code changes.
11.1 Every major frontier model integrates
The assistant's foundation-model slot is provider-pluggable. Out of the box it speaks three provider surfaces — the Anthropic API, AWS Bedrock and GCP Vertex AI — which between them serve the major frontier families: Claude, Gemini, Llama, Mistral and the rest of each catalog. Beyond the built-in providers, the model registry accepts any model behind an HTTPS endpoint, so Azure AI Foundry in your own tenant, a frontier model reached through your own gateway, an open-weights model you host, or a fine-tune you trained all join the same way — and a model registered that way is not a second-class citizen: it lands in the same slot, under the same audited lifecycle, with the same permissions. Which model is live is a Control panel decision: register it, enable it, activate it into the slot — every step audited — and deactivating falls back to the built-in default rather than to silence. The demo on this site happens to run Claude Sonnet 5; a deployment runs whatever you choose, under your own BAA with your AI provider.
11.2 Bring, build and extend your own models
The registry is not only for language models. Nine model kinds are first-class — foundation, retrieval, deterministic, predictive, classifier, imaging, optimization, mapper, custom — and fifteen capability slots wire them into the product: assistant, claims, prior-auth, coding, ROI, no-show, segmentation, ambient (the self-hosted speech listener), triage, scheduling, ingest, imaging, trials, measures, and other. Activate your own denial-risk model into the claims slot and the Claims & billing screen scores with your model; the built-in core steps aside but stays registered, one click from reinstatement. Every model in a slot automatically inherits the platform's management surface: lifecycle audit events, live performance metrics, drift recognition against the population it serves, and alerting that never closes itself.
11.3 Step by step: bringing your own model — with audit and tracking on
The lifecycle below is the only path a model takes into production, and it is the same for a frontier LLM behind a gateway, a vendor's risk model, and a classifier your own team trained. Following it is what enables PHI AI's required audit and tracking — a model that skips a step simply never serves a slot, so there is no untracked way in.
- Prepare the endpoint. Expose the model behind HTTPS (the registry rejects non-TLS endpoints at registration). Decide its capability slot up front — the slot determines which feature it powers and which metrics will watch it. Your endpoint must be stateless with respect to PHI: it receives the request, returns the inference, and retains nothing — the same posture your BAA with the model's provider must cover.
- Register it (Control panel → Model registry → Register): name, kind (foundation · retrieval · deterministic · predictive · classifier · imaging · optimization · mapper · custom), slot, provider, model ID, version, endpoint URL, and its stated purpose. Registration writes system.model_registered to the audit trail naming the model — tracking begins before the model serves anything.
- Enable it (system.model_enabled). Enabled means eligible, not live: the slot still runs its current model. This is the state for validation against your own data.
- Activate it into its slot (system.model_activated, recording the slot). The feature screen switches to your model immediately, and its AI-core banner says so: "activated from the registry." The built-in core steps aside but stays registered — deactivating falls back to it, never to silence. The assistant slot additionally requires kind foundation and a model ID, because that slot is live-wired to the provider API.
- Verify the tracking took hold. Open the feature's screen, then the audit trail: every invocation of the slot now writes model.inference naming your model and version (e.g. claims/pending-queue n=25 model=YourRiskModel v2.1). Assistant questions carry model= on their ai.query events the same way. This is not optional instrumentation you add — the platform writes it at every slot invocation, whichever model is active.
- Confirm monitoring attached. Model analytics & monitoring computes the slot's performance metrics and population-stability drift for whatever model serves it — your model inherits the watch the moment it activates. Alerts fire on the same thresholds, are acknowledged under a name (model.alert_acknowledged), and never close themselves.
- Operate it like everything else. Disable (system.model_disabled) degrades the feature honestly with the switch named on its screen; delete (system.model_removed) requires the model be non-builtin and falls the slot back first. Every state change is an audited event under the operator's name — the model's whole life on the platform is reconstructible from the trail.
What PHI AI requires of you, summarized: a TLS endpoint, a truthful registration (kind, version, purpose), your own BAA with the model's provider for any PHI-reading slot, and validation before activation. What PHI AI guarantees in return: lifecycle audit, per-invocation model.inference attribution, live metrics, drift recognition, and alerting — for every model, built-in or brought, with no additional integration work.
11.4 Extend the features themselves
Features are built against slots and permissions, not against models or roles — which is what makes them extensible. A new feature declares the capability slot it draws from and the permission that gates its screen; the registry, monitoring, refusal and audit machinery attach to it for free. The same pattern governs what already ships: when a System Administrator disables a slot's model, that feature degrades honestly on its own screen with the switch named, and everything else keeps working. Roles compose the same way — screens check permissions, never role names, so your organization's identity system can define new roles without a code change anywhere.
11.5 The configuration surface, in one place
Everything below is live-configurable from the Control panel and the Source & target EMRs screen, and every change writes config.changed (or its own system.* event) under the operator's name:
- Models: the active model per capability slot, the assistant's live-call and retrieval switches, the answer-length bound, and the full registry lifecycle.
- Integrations: source and target EMR vendor, base URLs, client IDs, the bulk group, and each outbound feed's paused/healthy state — with independent SSO per side.
- Deployment shape: the small/large storage profile, retention periods with per-resource-type overrides, and the per-cloud infrastructure variables (all set at deploy time, all documented in Setup & deployment).
- Identity: proxy SSO, SMART launch, or local accounts — with roles asserted by your identity source of truth.
The regulatory frame for all of the above: Compliance & responsibility. Deployment sizing and infrastructure: Setup & deployment.
Full architecture with data flows
The whole platform on one page — an AI-native architecture, designed from first principles for AI use of protected data: the animated map first, then a component-by-component reference, the trust boundaries, the deployable platform's cloud architecture, and the failure-mode table. Watch the dots: each moving dot is data traveling — green flows are records arriving, navy flows are people and the AI reading under the platform's rules, orange flows are records leaving, and every faint gray thread reports to the audit trail. Hover over any box for a plain-English explanation.
Records flow from the source EMRs through ingestion into the encrypted Data Store, where sensitive categories are fenced off. People and the AI assistant read through role gates; drafts return to the EMR only with a human signature; exports pass consent gates before anything leaves. Every box reports to the audit trail at the bottom — including the gates' refusals.
1. How to read the picture
- Nothing skips the gates. There is no path from the Data Store to a person, the AI, or another organization that does not pass a role, consent or signature gate. If you trace any navy or orange arrow backward from its destination, you pass through a gate before you reach the data.
- The AI is inside the walls. PHI RAG reaches the Data Store only through the same audited, role-scoped tools people use — the model never holds a database connection, and the foundation model (the LLM you bring — Claude Sonnet 5 in this demo) receives only the excerpts the gates released for the current question.
- The audit trail sees everything. Every arrow that moves data — and every arrow a gate refuses — lands on the hash chain. The trail records references, never content.
- Green in, orange out. Arrival and departure are different disciplines: arrival is about completeness (watermarks, checkpoints, surfaced gaps), departure is about permission (consent gates, exclusion counts, refusals).
2. Component reference
2.1 Source EMRs
- Role: data planes into the AI-native platform — Epic, Oracle Health, athenahealth, eClinicalWorks, MEDITECH, NextGen. Records arrive from them; signed documentation delivers back to them (the chart of legal record, as the law requires — the intelligence, governance and AI-ready data model live in this platform).
- Inputs: signed write-back and configured deliveries.
- Outputs: bulk extracts and streaming events.
- Failure mode: vendor unavailability stalls ingestion — runs fail visibly, watermarks hold, and nothing downstream silently ages.
2.2 Bulk import
- Role: population-scale arrival over the FHIR Bulk Data API.
- Honors: each vendor's real seams — Epic's one-run-per-24h group export with no incremental mode, NextGen's absence of bulk entirely.
- Emits: bulk.completed / bulk.refused.
- Key property: the watermark — the timestamp of the last known-complete extract — advances only on a clean run, so a half-arrived population can never masquerade as the current state.
2.3 Streaming intake
- Role: real-time arrival — ADT admissions and discharges, ORU lab results, X12 835 remittance — on partitioned feeds.
- Key property: per-partition acknowledged checkpoints; a gap between checkpoint and latest offset is displayed with its exact range on the Streaming data screen.
- Deliberate non-feature: gap interpolation. A pipeline that guesses what it missed is a data-integrity incident that has not been discovered yet.
2.4 Encrypt · store · index
Role: the only doorway into the Data Store. Every record — bulk or streamed — is (1) encrypted, (2) classified for sensitivity (sud_part2, mental_health, reproductive, hiv, genetic, or none; psychotherapy content routed to its separate store), (3) normalized (reference resolution, terminology mapping), and (4) indexed for retrieval. Key property: classification happens at the door, not at read time — a record's sensitivity is a stored fact, uniform for every downstream gate.
2.5 The Data Store
- Role: the encrypted holdings — clinical, financial and behavioral-health record classes (the full inventory with live counts is on the Data Store screen).
- Key properties: no direct read path exists; every consumer goes through the gates; the psychotherapy store is physically separate; six integrity checks run live on the Data Store screen, including full audit-chain verification.
2.6 Role · consent · purpose gates
- Role: the enforcement point every read crosses. Evaluates: the profile's permissions (enumerated, no wildcards except the System Administrator's audited *), the record's sensitivity class against the reader's role and the applicable consent, and the session's purpose of use.
- Outputs: released records, plus a withholding count for whatever was held back.
- Emits: phi.disclosure-family events on release, access.denied on refusal. Key property: the gate cannot be bypassed by the AI — the assistant's tools call the same gate code the screens do.
2.7 PHI RAG (retrieval-tuned LLM)
- Role: the assistant. Plans retrieval, calls role-scoped tools, assembles gate-released excerpts, and produces cited answers.
- Contract: answer only from retrieved records; cite as [MRN-xxx · chart]; never assert absence from unretrieved documents; confirm before population sweeps; state withholdings; counts, never lists, on the population plane.
- Controls: the Control panel's retrieval switch (off = the assistant refuses to read the Data Store at all and says so).
- Emits: ai.query per question, plus the tools' own events.
2.8 The foundation model — yours
- Role: the foundation language model PHI RAG runs on, called over the model API at temperature 0 with bounded output.
- Bring your own: this demonstration runs Claude Sonnet 5, but the slot is provider-pluggable — a deployment activates the LLM the organization chooses, under its own BAA with its AI provider.
- Receives: the system contract, the last six turns, the role's tool schemas, and gate-released excerpts — never a database connection, never bulk data.
- Controls: the live-calls switch; off degrades to the scripted fallback.
- Data terms: excerpts are used to answer the question, not to train models, under the organization's agreement with the provider.
2.9 People & screens
- Role: six profiles across the role model, each seeing exactly the screens its permissions allow (the full matrix is in the system documentation).
- Key property: screens check permissions, not roles — role composition changes without touching screens.
2.10 Human signature
- Role: the signature rule's enforcement point. AI drafts queue; a licensed reviewer edits and signs; only the signed artifact acquires a write path to the EMR.
- Emits: signature.committed. Deliberate non-feature: auto-commit, batch signing, or timeout-advance. A draft's only exits are a signature or a return.
2.11 Export managers
- Role: the only doorways out — bulk delivery and outbound feeds. Apply the consent & redisclosure gate, record exclusion counts, honor the destination's write surface, and track acknowledgment.
- Emits: export.completed / export.refused.
- Key property: the three outcomes (complete / failed / refused) are all first-class and all visible in run history.
2.12 The audit trail
Role: the append-only, hash-chained memory of everything above. Every component writes to it; nothing edits it; the Control panel re-derives the full chain on every load and reports intact or the first broken row. In the deployable platform, concurrent writers may fork the chain legitimately; verification understands forks and distinguishes them from tampering.
3. Trust boundaries
Five boundaries, and what is allowed to cross each:
| Boundary | Crosses it | Never crosses it |
|---|---|---|
| Vendor EMR ↔ platform | Authenticated bulk extracts, streaming events, signed write-back, configured deliveries | Unsigned generated content; credentials in either direction beyond the OAuth flows |
| Ingestion ↔ Data Store | Encrypted, classified, indexed records | Unclassified records; unencrypted records |
| Data Store ↔ readers (people or AI) | Gate-released records with withholding counts | Direct queries; sensitive records for excluded roles; psychotherapy content for anyone but the Psychotherapy role |
| Platform ↔ model provider | The contract, the question, gate-released excerpts for this question | Bulk data; a database connection; credentials; records the gate withheld |
| Platform ↔ external recipients | Consent-gated packages with recorded exclusions; acknowledged feed events | Psychotherapy notes (ever); Part 2 content without Part 2 consent; partial exports presented as complete |
4. The deployable platform's cloud architecture
The platform ships this architecture hardened:
- Envelope encryption. Every stored object is encrypted with its own data key; data keys are wrapped by a KMS master key that never leaves the key service. Compromising storage yields ciphertext; key use is itself logged.
- Isolated stores. Record objects, audit events, and psychotherapy content live in separate stores with separate access policies — the psychotherapy separation is infrastructure, not a WHERE clause.
- Relational index + OMOP. A relational index serves the screens; an OMOP common-data-model layer serves research-grade analytics on the de-identified plane.
- IAM enforcement in depth. Roles are enforced in the application and as cloud IAM policy, with the purpose of use carried as an IAM session tag — the storage layer itself can refuse a read whose purpose tag is absent.
- A no-script web tier. The product interface runs under a Content-Security-Policy of script-src 'none': the screens execute no JavaScript, eliminating the XSS class outright. (This documentation's animated diagrams are the one scripted surface, and they render no PHI.)
- Credential hygiene. Model-provider and vendor credentials live server-side in restricted stores, never in pages, never in the repository.
5. Failure modes
What breaks, what happens, and what is designed to never happen:
| Failure | What happens | What never happens |
|---|---|---|
| Bulk download interrupted | Run marked failed; watermark holds; retry is a fresh run | A partial population presented as current |
| Stream broker restart | Gap surfaced with exact offsets; processing resumes from checkpoint | Interpolated events; silently skipped offsets |
| Model API down | Error stated; scripted fallback answers, labeled; questions still audited | Unlabeled degraded answers; lost audit events |
| Export destination rejecting | Bulk: run failed. Streaming: held with retry, backlog counted | Dropped deliveries; regenerated-and-duplicated deliveries |
| Gate cannot evaluate (missing consent state) | Deny, with the rule named; access.denied | Fail-open release |
| Audit chain broken | Verification reports the first bad row on the Control panel and Data Store screens | Quiet acceptance of a tampered log |
| Session identity no longer exists | Falls back to the default (least-privileged clinician) profile | A role-less or elevated ghost session |
The individual journeys — question to answer, records arriving, records leaving, draft to signature — are animated step-by-step in Data flow diagrams.
Data flow diagrams
The six journeys data makes through the AI-native platform, animated and then walked through step by step — with the audit event written at every step and the failure branches spelled out. Notice that the AI sits inside every flow, never outside the gates: this is what a platform designed for AI use of protected data looks like in motion. Dots are data on the move; a red branch is a refusal, and the refusal is always recorded, never silent. Hover over any box for a plain-English explanation.
A user asks a question. The assistant checks their role and purpose, retrieves only the records their permissions allow (the withholding gate counts what it holds back), sends those excerpts to the model, and returns an answer with citations.
Walkthrough
| # | Step | What happens | Audit event |
|---|---|---|---|
| 1 | The question | A signed-in profile asks. Their identity, roles and ambient purpose of use travel with the question. The question is stored in prompt history (questions only — answers are never stored). | ai.query |
| 2 | Retrieval planning | PHI RAG selects among the tools its role's schema contains. A tool the role lacks is absent, not forbidden — the model cannot even attempt it. | — |
| 3 | The gate | Each tool call passes the same role/consent/purpose gate a screen does. Sensitive-category records the reader may not see are withheld and counted. | phi.search / phi.disclosure / analytics.query |
| 4 | Retrieval | Gate-released excerpts come back as tool results. Population tools return counts, never lists (the counts-never-lists rule). | — |
| 5 | Drafting | The foundation model (yours — this demo runs Claude Sonnet 5) writes the answer from the excerpts, at temperature 0, citing each source as [MRN-xxx · chart]. It may not characterize documents it did not retrieve. | — |
| 6 | The answer | The cited answer renders, including the withholding statement ("3 mental-health records were withheld from this view"). The reader knows what the answer could not see. | — |
Failure branches: a population-wide question pauses for a minimum-necessary confirmation before step 3; a request to enumerate a cohort is refused with the counts-never-lists rule named; retrieval disabled (Control panel) refuses at step 2 with the switch named; a model failure at step 5 reports the error — steps 1–4's audit events are already on the chain, so the trail shows exactly what was read even for a failed turn.
A bulk run asks the source EMR for its patient group, polls until ready, downloads the record files, and encrypts, stores and indexes every record.
Walkthrough
| # | Step | What happens | Audit event |
|---|---|---|---|
| 1 | Kickoff | Group/[id]/$export against the source EMR, asynchronous. On Epic the group is a provisioned Group FHIR ID and the window is one run per 24 hours per client. | — |
| 1a | Rate-limit branch | A kickoff inside the window is refused by the platform, before the vendor sees it, and the refusal is itself a recorded run in the manager's history. | bulk.refused |
| 2 | Status poll | The status URL returns progress until the manifest is ready. Bulk export has no incremental mode on Epic — every run is a full re-extract. | — |
| 3 | Download | NDJSON files, one resource type per file. An interruption at any file marks the whole run failed. | — |
| 4 | Encrypt · store · index | Reference normalization, sensitivity classification at the door, encryption, indexing. Only now, and only for a clean run, does the watermark advance. | bulk.completed |
The watermark rule, precisely: the watermark is the timestamp of the last run that completed every file. A failed run leaves it untouched, so every downstream consumer — screens, the assistant, analytics — can state exactly how fresh its world is, and a half-arrived extract can never impersonate the current population. The System Administrator can release the 24-hour hold from the Control panel; the override writes system.hold_released, because an override that leaves no trace is a vulnerability, not a feature.
An export names its scope and destination, then passes the consent gate. What passes is delivered and acknowledged; what is refused is a recorded refusal, not a quiet omission.
Walkthrough
| # | Step | What happens | Audit event |
|---|---|---|---|
| 1 | The request | Scope (full population / cohort / one patient), destination (target EMR, HIE, registry, payer, ROI requester), format (FHIR NDJSON or C-CDA). | — |
| 2 | The consent & redisclosure gate | Psychotherapy notes are never in scope. Part 2 content without Part 2-specific consent refuses the export. Other sensitive categories are excluded per the data-sharing agreement — excluded, and counted. | — |
| 2a | Refusal branch | The gate's no is a first-class outcome: the run appears in history as refused with its reason, exactly where an auditor will look. | export.refused |
| 3 | Packaging | The allowed set is packaged with its exclusion counts recorded — the withheld records are numbers in the run history, never a silent gap the recipient cannot see. | — |
| 4 | Delivery | On the destination's write surface. A destination with no bulk write path (NextGen) refuses at this step rather than degrading into thousands of unrequested individual writes. | export.completed |
| 5 | Acknowledgment | Streaming deliveries track acknowledged vs produced sequence. Held = queued and accounted, retried without regenerating. Paused = a governance decision that accumulates and does not leak. | — |
The one path by which generated content reaches the legal record — and the human decision standing in the middle of it.
Walkthrough
| # | Step | What happens | Audit event |
|---|---|---|---|
| 1 | The material | A visit happens; ambient documentation or a summarization request produces the raw material. | ai.query + phi.disclosure |
| 2 | The draft | PHI RAG drafts the note from retrieved records, labeled as a draft, carrying its citations. | — |
| 3 | The queue | The draft waits in the signature queue. It has no write path from here — that is the signature rule, structurally. | — |
| 4 | The human | A licensed reviewer reads the draft against its citations, edits it, and decides. On signing, the content becomes the signer's — clinically and legally. | signature.committed |
| 4a | Return branch | A draft not accepted is returned or discarded. Drafts never advance by timeout, default, or batch approval. | — |
| 5 | Write-back | Only the signed note is delivered to the EMR (on Epic, a DocumentReference create), attributed to the signer. The EMR remains the legal record. | — |
A genuine vision inference over the frames: the registry's imaging-slot model describes what it can actually see, measures from the acquisition scale, and its draft waits for a licensed reader like every other draft.
Walkthrough
| # | Step | What happens | Audit event |
|---|---|---|---|
| 1 | The study | A reader opens a study in the viewer; the open is a recorded disclosure, and the context strip follows the study's patient. | phi.disclosure |
| 2 | The frames | Representative frames of the series are rendered and sent to the imaging-slot model — pixels, the acquisition scale, and the problem list; never an answer key. | ai.query |
| 3 | The read | The vision model describes only what is visible, measures discrete findings in mm, names the frame each appears on, and drafts TECHNIQUE / FINDINGS / IMPRESSION. | model.inference |
| 4 | The cache | One read per study, ever: the result is cached against the study and generator version, so every later viewer reads the same draft at no model cost. | — |
| 5 | The draft | Filing the read creates an unsigned draft report on the study's patient — into the signature queue, never past it. | ai.draft_created |
The consent gate decides before one word is captured; the self-hosted listener transcribes inside the deployment; the note files to the visit's own patient and waits for a signature.
Walkthrough
| # | Step | What happens | Audit event |
|---|---|---|---|
| 1 | The gate | Before capture: is recording this visit lawful in this jurisdiction, for this modality, with this consent? An all-party state holds capture until the visit-level attestation exists. | consent.evaluated / consent.attested |
| 2 | The capture | Record / pause / stop. Audio stays inside the deployment — the listener is self-hosted, so no ASR vendor ever touches a voice and no vendor BAA exists because none is needed. | ambient.capture_opened |
| 3 | The transcript | The listener's genuine output, speaker-labeled, imperfections included — the drafting model flags garbled terms for the signer rather than guessing. | model.inference |
| 4 | The note | A SOAP draft from the transcript, filed to the visit's own patient — never to whoever is in context, never to a stand-in. Unresolvable patient → loud refusal, no draft. | ai.draft_created |
| 5 | The signature | The draft waits in the signature queue; on signing it becomes the clinician's note and only then acquires a write path. | signature.committed |
What never happens, on any of these flows
- Data released without a gate evaluating the reader, the record and the purpose — and writing the event.
- A withholding, exclusion or refusal that is invisible to the person looking at the result.
- A partial arrival or partial delivery presented as complete.
- Generated content entering the legal record without signature.committed above it in the chain.
- Psychotherapy content in any export, to any destination, under any scope.
The components these flows run across are documented in Architecture & data flows; the exact API calls are in API details.
API details
Every programmatic surface of the AI-native platform, in depth: the conventions all of them share, the retrieval tools the AI assistant calls (with request and response shapes), the model API behind it, the application's own route surface, the EMR-facing integration APIs records arrive and leave through, and how the platform authenticates to each vendor. These APIs were designed for AI as the consumer — audit-before-data, role-scoped tool schemas, structured refusals — not adapted to it afterward.
1. Conventions shared by every surface
- Audit before data. Every surface writes its audit event before returning data. A crashed request leaves its intent on the trail; there is no window in which data moved but no record says so.
- Permission checks name their permission. A refusal states the missing permission (integration:view, system:admin, …) and writes access.denied with that permission as the object reference.
- Refusals are data, not errors. A consent refusal, a rate-limit refusal or a write-surface refusal returns a structured outcome (status: refused, with the reason) — it is a legitimate result the caller is expected to display, not an exception to swallow.
- State-changing calls are POST + CSRF. Every mutating route requires the session's CSRF token; GETs never mutate.
- Purpose travels. The session's ambient purpose of use is recorded with every event a surface writes.
2. The assistant's retrieval tool API
PHI RAG reaches the Data Store only through eight narrow tools. The tool schemas are generated per role: a profile lacking a permission does not get a degraded tool — the tool is absent from the schema entirely, so the model cannot even attempt the call. Every tool audits before returning.
2.1 search_patients
Requires patient:search · audits phi.search
Token-wise AND match over names and MRNs — every word in the query must match, in any order. Demographics only; no clinical content.A worked example against this site's synthetic dataset is in Emulators & non-PHI setup.
{"name": "search_patients", "input": {"query": "jane sample"}}
→ [{"id": 0, "mrn": "MRN-######", "name": "Jane Q. Sample",
"birth_date": "YYYY-MM-DD", "gender": "female", "payer": "…"}]
2.2 get_patient_chart
Requires patient:read · audits phi.disclosure
The storyboard in structured form: demographics, active problems, medications, allergies, recent encounters. Sensitive-category rows are withheld and counted — the response carries a withheld_from_retrieval block naming each category and its count, which the assistant is required to state in its answer.
{"name": "get_patient_chart", "input": {"patient_id": 214}}
→ {"patient": {...}, "conditions": [...], "medications": [...],
"encounters": [...],
"withheld_from_retrieval": {"mental_health": 3, "sud_part2": 1}}
2.3 get_labs
Requires patient:read · audits phi.disclosure
Lab and vital series from the curated LOINC panel, optionally filtered to one analyte, with abnormal flags computed from reference-range logic (A1c ≥ 6.5%, systolic ≥ 140 mmHg, LDL ≥ 160 mg/dL, BMI ≥ 30, …).
2.4 read_document
Requires patient:read · audits phi.disclosure
One clinical note's full text. A document classified into a sensitive category is refused for roles the rule excludes; the refusal is returned as a structured withholding (category named, content absent) and audited. This is the tool behind the never-assert-absence rule: the model may only characterize documents this tool has actually returned.
2.5 get_claims
Requires claims:read · audits phi.disclosure
The financial plane for one patient: claims with payer, billed / allowed / paid amounts, adjudication status, and CPT-coded service lines. Absent for profiles without claims:read — a clinician's assistant genuinely does not have this tool.
2.6 count_cohort
Requires analytics:query · audits analytics.query
Condition-matched population counts. Returns a number, never a list — the tool is structurally incapable of enumerating patients, which is how the counts-never-lists rule is enforced at the API layer rather than by asking the model nicely.
{"name": "count_cohort", "input": {"condition": "diabetes"}}
→ {"condition": "diabetes", "patient_count": 541}
2.7 list_psychotherapy_notes / 2.8 read_psychotherapy_note
Require psychotherapy:read · audit psychotherapy.list / psychotherapy.disclosure
Present only in the Psychotherapy role's tool schema. Listing returns session metadata; reading returns one session's content — and the assistant is instructed to obtain an explicit minimum-necessary confirmation from the user before reading session content even for this role. Both events name the specific note in the audit trail, which is what an accounting of disclosures for psychotherapy content requires.
3. The model API
3.1 Request anatomy
PHI RAG calls the Anthropic Messages API. Each turn sends: the system prompt (the retrieval contract, the citation format, the refusal rules, the role's identity and purpose), the last six conversation turns, the role's tool schemas, and the user's question:
POST https://api.anthropic.com/v1/messages
x-api-key: [server-side credential — never in a page]
anthropic-version: 2023-06-01
{"model": "claude-sonnet-5",
"max_tokens": 1500,
"temperature": 0,
"system": "You are the PHI AI assistant... [contract]",
"tools": [ ...role-scoped tool schemas only... ],
"messages": [ ...last 6 turns..., {"role": "user", "content": "..."} ]}
3.2 The tool loop
- The model responds with either final text or a tool_use block naming a tool and its input.
- The platform validates the tool exists for this role, executes it server-side (audit first, then query, then withholding), and returns a tool_result block.
- The loop repeats — typically one to four tool calls — until the model emits final text. The loop is bounded; a runaway loop terminates with the turn's partial work discarded and the query already audited.
- The final text is rendered with its citations. Answers are not stored; only the question enters prompt history.
3.3 Model parameters and failure behavior
- temperature 0 — deterministic drafting; clinical answers should not vary with the weather.
- Bounded output and a six-turn context tail — the model never accumulates an unbounded PHI context.
- Failure ladder: retrieval disabled → structured refusal; live calls off / no key → deterministic scripted fallback, labeled; API error → the error is stated, the question was already audited, nothing was read.
4. The application's route surface
The application's own HTTP surface, useful as a map of what exists. Every route is /?p=<page>; every mutation is a POST with action and CSRF token.
| Route / action | Method | Requires | Writes events |
|---|---|---|---|
| p=assistant | GET | assistant:use | — |
| action=ask | POST | assistant:use | ai.query + per-tool events |
| action=prompt (save/unsave/delete) | POST | assistant:use | — |
| p=patients / p=patient | GET | patient:search / patient:read | phi.search / phi.disclosure |
| p=document / p=psych_note | GET | patient:read / psychotherapy:read | phi.disclosure / psychotherapy.disclosure |
| p=claims / p=claim / p=priorauth | GET | claims:read | — |
| p=cohort | GET | analytics:query | analytics.query |
| action=sign | POST | patient:read | signature.committed |
| action=roi | POST | roi:manage | roi.fulfilled / roi.denied |
| p=audit | GET | audit:read | — |
| p=bulkimport / p=streaming / p=bulkexport / p=streamexport | GET | integration:view | — |
| action=run_bulk | POST | admin:config | bulk.completed / bulk.refused |
| action=run_export | POST | admin:config | export.completed / export.refused |
| p=emrconfig / action=save_config | GET / POST | admin:config | config.changed |
| p=controlpanel | GET | system:admin | — |
| action=sys_rag / sys_assistant / sys_feed / sys_release_hold | POST | system:admin | system.* |
| p=docs / p=datastore / p=segmentation / p=consent / p=spec… | GET | none | consent.evaluated (consent page) |
5. Ingestion APIs — records arriving
5.1 FHIR Bulk Data Export (population import)
The only sanctioned way to ingest an entire population. The flow against the source EMR:
- Kickoff. GET [base]/Group/[group-id]/$export?_type=Patient,Encounter,Condition,… with Prefer: respond-async and Accept: application/fhir+json. The server answers 202 Accepted with a Content-Location status URL. On Epic, the group is a Group FHIR ID provisioned with the app registration, and a group is limited to one export per 24 hours per client — a second kickoff is refused by the platform before it ever reaches the vendor, and the refusal is a recorded run.
- Status polling. GET [status-url] returns 202 with an X-Progress header while running, then 200 with a manifest:
{"transactionTime": "2026-08-27T01:00:05Z",
"request": ".../Group/eGrp-7ac2/$export",
"requiresAccessToken": true,
"output": [
{"type": "Patient", "url": ".../file-01.ndjson"},
{"type": "Encounter", "url": ".../file-02.ndjson"},
{"type": "Condition", "url": ".../file-03.ndjson"}, ...]}
- Download. Each file is newline-delimited JSON, one FHIR resource per line, fetched with the access token. urn:uuid:-style references are normalized to resource-relative references during ingestion so the record graph links correctly.
- Encrypt · store · index. Every resource is encrypted, classified for sensitivity (§6.2 of the system documentation), and indexed. Only a run that completes every file advances the watermark — the marker of the last known-complete extract. A dirty run is marked failed and the watermark holds, so nothing downstream ever believes a partial extract was the world.
5.2 Streaming intake
Three inbound feeds, each partitioned with acknowledged checkpoints:
- ADT — admissions, discharges, transfers. Near-real-time census and the trigger for downstream notifications.
- ORU — lab results as they finalize.
- X12 835 — payment remittance, feeding the claims plane.
The checkpoint protocol: each partition tracks checkpoint_offset (last event fully processed) and latest_offset (newest event seen). Processing advances the checkpoint only after the event is durably stored. A gap — offsets seen but unacknowledged, as after a broker restart — is surfaced on the Streaming data screen with the exact offset range, and is never interpolated: an interpolated gap is a silent data-integrity failure wearing a healthy pipeline's clothes.
6. Export APIs — records leaving
6.1 Bulk delivery
A bulk export names a scope (full population, cohort, or one patient), a destination (target EMR, HIE, registry, payer, or an ROI requester) and a format (FHIR NDJSON or C-CDA). The pipeline: assemble the scope → consent & redisclosure gate (psychotherapy notes never included; Part 2 content refuses without Part 2-specific consent; other sensitive categories excluded per the data-sharing agreement, with counts) → package with exclusion counts recorded → deliver on the destination's write surface → mark complete only if everything delivered. Outcomes are three, all recorded: complete (with records-delivered and records-withheld counts), failed (destination rejected or transfer interrupted — never presented as partially complete), and refused (the gate said no; the refusal carries its reason).
6.2 Write-back (signed documentation)
The one path by which generated content reaches the EMR: a draft signed in the signature queue (signature.committed) is delivered as a vendor-appropriate write — on Epic, a DocumentReference create carrying the signed note, attributed to the signer. Unsigned content has no write path; there is no API to force it.
6.3 Outbound feeds
Streaming exports track delivered_seq (acknowledged by the destination) against latest_seq (produced locally). The difference is the held backlog: held retries against the destination without regenerating or skipping; paused is a governance decision (from the Control panel) that accumulates sequence and leaks nothing; resuming delivers from the checkpoint. The de-identified research feed carries no identifiers by construction.
7. Vendor authentication
Per-vendor detail with inline citations to each vendor's published documentation lives in EMR connections.
7.1 SMART Backend Services (Epic)
- The platform registers as a backend service and holds a private key; the EMR holds the public key.
- Per token request, the platform signs a JWT (RS384) with claims iss = sub = client ID, aud = the token endpoint, exp ≤ 5 minutes, single-use jti.
- POST [token-endpoint] with grant_type=client_credentials, client_assertion_type=…jwt-bearer, the signed assertion, and scope=system/*.read (plus write scopes only where write-back is configured).
- The short-lived access token authorizes the bulk and FHIR calls; tokens are never persisted beyond their life.
7.2 The other vendors — flows per their own documentation
Oracle Health: two documented modes — client credentials (secret in a Basic header) or a signed JWT assertion, with the JWT mode the documented path for bulk; every resource scope must be requested explicitly (wildcards unsupported), so a missing scope fails at the resource and conformance is verified per-resource. eClinicalWorks: the same asymmetric private-key JWT assertion as Epic — no client secret. athenahealth: OAuth2 client credentials with a client secret — the one profiled vendor whose documented flow is a secret. MEDITECH / NextGen: SMART Backend Services recorded as the ONC g(10) certification baseline and flagged for portal confirmation; NextGen exposes no documented bulk export and no bulk write surface — population ingestion falls back to per-patient reads, and a bulk export aimed at it is refused with the seam named. Full citations per vendor: EMR connections.
8. Error model
| Condition | Behavior | On the audit trail |
|---|---|---|
| Missing permission | 403; the page names the permission | access.denied |
| Consent absent (Part 2, psychotherapy, recording) | Structured refusal with the rule named | access.denied / export.refused / consent.evaluated |
| Vendor rate limit | Run refused before reaching the vendor | bulk.refused |
| Destination without a write surface | Export refused, seam named | export.refused |
| Transfer interrupted | Run marked failed; watermark holds | bulk.completed (status failed) |
| Model API failure | Error stated; nothing read; fallback offered | ai.query (already written) |
| Stale session identity | Falls back to the default profile | — |
The architecture behind these surfaces is drawn in Architecture & data flows; the journeys are animated step-by-step in Data flow diagrams.