AI MedAgent
EHR integration on FHIR · Technical datasheet · v0.3, 25 August 2026

Not an interface developer? The same findings are written without the acronyms in The connection was the easy part, six pages, no prior knowledge assumed. It explains what a 403 is, why an empty refusal is worse than a rude one, and what the vendors’ own demonstration patients actually contain.

What this document adds

The AI MedAgent technical datasheet describes the method. This one answers the question every evaluator asks within ten minutes: can it reach the chart. It now answers it with measurements rather than with capability statements, because the two turned out to disagree in ways that matter.

It sets out what three EHR vendors will actually hand a third party, what each one costs to reach, what arrived when we asked, and the second question that follows immediately: the chart is not a live stream, so where does continuous infusion and ventilator status come from.

The integration, in one picture

SOURCES, AND WHAT EACH ONE COSTS TO REACH Epic on FHIR R4 self-service registration, RS384 JWT assertion read and measured Oracle Health Millennium R4 open sandbox. No credentials of any kind read and measured MEDITECH Expanse, US Core R4 registration, signed EULA, human approval documented, not yet measured Synthea, MITRE synthetic. Carries the depth neither sandbox has read and measured One reader per vendor One record shape Patient, Encounter, Observation, MedicationRequest, Condition, AllergyIntolerance LOINC canonicalization, because vendors disagree about words and agree about codes. AI MedAgent integrity screen, first 8 screening classes AI MedAgent Reasoning coverage counted and stated on every call the engine cannot tell which source it is reading CDS Hooks card, in the order screen order-select, an HL7 standard. Live at aimedagent.net/cds-services Findings, the reasoning, and a written statement of the checks not performed. No write-back to the chart. Nothing is filed, nothing is signed, nothing moves without a clinician. NOT AVAILABLE THROUGH ANY EHR FHIR API MEASURED HERE Continuous infusion status. Ventilator status. The chart carries what was ordered and what was charted. It does not carry what the channel is doing now. The documented route is still a per-customer HL7 v2 flowsheet build. See the section below.
Three EHR vendors and one synthetic source, one record shape, one engine. The reader per vendor is the only part that differs, and it exists so that nothing downstream has to know which source it is holding. The device gap at the bottom is not a gap in this design; it is a gap in what any EHR API exposes.

What each vendor costs to reach

This is the first thing anyone building on top of an EHR needs and the last thing anyone publishes. The three vendors sit in three different tiers, and the difference is not technical.

VendorWhat it takes to get inTime to a first readStatus here
Oracle Health
Millennium R4 open sandbox
Nothing. No account, no key, no agreement. An unauthenticated GET returns patient data.Under an hourRead and measured
Epic
Epic on FHIR R4 sandbox
Self-service registration. Publish a JWK Set, sign an RS384 client assertion, exchange it for a token. No human in the loop.Same dayRead and measured
MEDITECH
Greenfield Workspace, US Core STU6
Registration, then a welcome packet from the Greenfield team, a signed End-User License Agreement, a Google ID, and an OAuth2 client ID and secret. Authorization Code grant only. The client credentials grant is not offered, so there is no unattended access.Gated on a human, and on a browser at every runServes all four resources Epic answers with 403

Correction, 24 August 2026. An earlier version of this sheet said Epic refuses four of seven resource types. That was wrong. Of the four 403s, only Condition and AllergyIntolerance had their scopes granted; Procedure, Immunization and DocumentReference were never requested, and DiagnosticReport was granted and served. Two is the correct number and it is still the finding. The error was found because a reader on chat.fhir.org asked whether the Patient read also returned 403. It does not: Patient, Observation, Encounter, MedicationRequest and DiagnosticReport all returned data for the same patient on the same token.

MEDITECH row measured 24 August 2026 against the live Greenfield Workspace, patient Sarai Mccall, 321 resources returned by a single Patient/$everything call. No fee is stated in the published EULA. Every figure in this row was read from the sandbox, not from the documentation.

What the APIs actually returned

Published capability statements say what a server supports. They do not say what it will give you. Those are different questions and only one of them can be answered by reading documentation.

ResourceEpic sandboxOracle open sandboxMEDITECH GreenfieldSynthea, for scale
Patientservedservedservedserved
Observation, vitals3711,000 paging stopped here151,126
Observation, laboratory5345337,766
MedicationRequest1500 paging stopped here51,113
Condition, problem listHTTP 403, zero-byte body. system/Condition.read was in the granted tokenserved, not counted10273
AllergyIntoleranceHTTP 403, zero-byte body. system/AllergyIntolerance.read was in the granted token1241 Penicillins, anaphylaxis0 recorded
DocumentReference403. Scope never requested. Correct behavior, not a finding199 paging stopped here100
Encounter10200 paging stopped here4453
Procedure403. Scope never requested. Correct behavior, not a finding56331,421

Read the MEDITECH column carefully too, for the opposite reason. Those figures are small because they are one real-shaped chart read in full, not a volume generator. The whole column came back in a single Patient/$everything call, 321 resources, and the bundle carried no next link, so nothing is truncated and no figure in that column is a paging cutoff. It is the only one of the four columns where the count and the total are the same number. MEDITECH is also the only server of the three that offered $everything at all.

Read the Oracle column carefully. Four of those figures are where the loader stopped paging at twenty pages, not how many records exist, and they are marked. More exist and were not read. Oracle answers Condition freely to an unauthenticated request, established by probe, but the bulk pull skipped it because Oracle ignores _count on that resource and the response runs to megabytes. A truncation point printed as a total is the same class of false statement this document is about, so it is not printed as one.

Epic authorizes below the OAuth scope, and its documentation does not say so

system/Condition.read sits in the granted token. The read still returns HTTP 403 with a zero-byte body, which is indistinguishable from a broken endpoint and gives a developer nothing to work with.

The answer came out of an error string on a different endpoint: "Combination of parameters is not valid for any authorized sub-resource." Epic authorizes at sub-resource level, beneath the scope. Four probes established it by measurement. It is not in the published documentation, and anyone planning a build against Epic should know it before month nine rather than after.

The practical consequence for a medication safety tool is severe and worth stating plainly. Epic proves the connection and then refuses the two resources a medication screen most needs, the problem list and the allergy list, having granted the scope for both. DocumentReference and Procedure also returned 403, but those scopes were never requested, so that is Epic behaving correctly and it is not part of this finding. DiagnosticReport was granted and served seven reports.

What arrived when we asked, and why it matters more than the count

An integration datasheet that only covers how to connect is half a document. The other half is what comes back, and in both certified sandboxes what came back would break a naive reader.

None of this is a criticism of a test environment, which is what a test environment is for. It is the reason the integrity screen runs before any clinical content and prints its findings above the clinical section rather than in a footnote. A system that calculates perfectly and understands nothing averages a 37 kg swing into a trend line and hands a clinician a number.

Every figure above is reproducible from the saved records and the loaders in this repository. A reference-range check catches none of them: 45 kg and 89 kg are both plausible weights and 37 °C is a normal temperature. What fails is the rate of change, the variance, and the sign.

How the integration works

Write-back was considered and rejected

An earlier draft of this document proposed filing the consult note to the chart as a DocumentReference. That path was abandoned on 21 August and the reasons are worth keeping.

The Epic sandbox is shared with every developer on fhir.epic.com, so writing to it pollutes somebody else's test. More importantly, a note filed into a chart proves less than a card that appears while the clinician is still deciding. Nothing in the current design writes anything to any chart.

What is built, what is proposed, what is unverified

ItemStatusNote
Backend Services authentication to EpicRunningRS384 client assertion, published JWK Set, five read scopes granted.
Unauthenticated read from Oracle HealthRunningFull record including the resources Epic refuses.
One record shape across three loadersRunningEpic, Oracle and Synthea. The engine cannot tell which it is reading.
Integrity screen, unit-awareRunning53 LOINC codes with impossibility bounds. Sign check is unit-independent.
Eight medication screening classesRunningText match only. See the drug knowledge socket below.
CDS Hooks serviceDeployedorder-select and patient-view, live at aimedagent.net/cds-services. Returns a coverage card on every invocation.
AI MedAgent Reasoning inside the endpointDeployedRuns after the deterministic screen. Hard timeout, and a card that says so when it does not complete.
Drug knowledge socketBuilt and emptyCalling it throws DRUGKB_NOT_CONNECTED, in public, on purpose. Seven of eight classes are unserved without a maintained drug knowledge base.
MEDITECHConnected 24 Aug 2026321 resources on one Patient/$everything call, including Condition and AllergyIntolerance, the two resources Epic grants a scope for and then refuses. Authorization Code grant only, so every run needs a browser and a person.
Device cloud feed for infusion and ventilator statusProposedNot in any EHR FHIR surface measured here. Not carried by IPEC. No standard subscription transaction exists.
Any use with real patient dataNot possible todayRequires a named customer of the vendor. This project does not seek one.

The other gap: where the live device data comes from

Everything above concerns the chart. The method reasons over a stream, and two of the signals it needs are not in any FHIR surface measured here: continuous infusion status and ventilator status. Epic’s documented route for infusion pump data remains an HL7 v2 flowsheet interface with row mapping negotiated per customer, and no vendor read for this document exposes a channel-level infusion state through FHIR, which is a custom build at the customer’s expense and delivers a flowsheet summary rather than a live channel state.

The standards do not close this gap either, and the detail matters. Citations are to the IHE Devices Technical Framework Rev. 10.0, Final Text, 4 November 2024. The domain was renamed from Patient Care Device, which is why transaction identifiers keep the PCD prefix.

So the direct feed is not a shortcut around the standard

It is the only place the required data currently exists. A continuous, channel-level view of what an infusion is actually doing is not defined by a Final Text transaction, is not exposed by the EHR, and is not obtainable without either a per-customer HL7 v2 build or a feed from the device manufacturer.

A manufacturer running an always-connected pump already holds this data in its own cloud. Publishing a read-only API over it would create the interface the standard has not yet defined, and would do it with a real device behind it rather than a specification.

What the reasoning engine needs from a device cloud

Read only. Advisory only. No control path of any kind is requested, offered or implied.

SignalShapeWhy it is needed
Channel infusion stateperiodic, per channelrate, dose, VTBI, volume infused, and running, paused, KVO or alarming. This is the signal IPEC does not provide.
Programmed against deliveredboth, kept distincta record of what was programmed is not a record of what was given, and collapsing them is the failure this whole method exists to address
Containment hierarchyMDS, then VMD or channel, then metrica multi-channel pump is one device with several channels. Flatten it and you lose which channel delivered which drug, which on a vasopressor and a maintenance fluid is the entire meaning of the message
Drug identity and concentrationwith the dose, alwaysrate is not dose. Converting mL/h to mcg/kg/min needs concentration and weight. A dose carried without its basis cannot be recomputed or audited
Event streamtimestampedthe three IPEC milestones, plus rate change and KVO transition if the platform can emit them. Those two are the valuable ones precisely because the standard omits them
Ventilator statusperiodicmode, set parameters and measured parameters. Same gap, different device
Time basesynchronized, source namedan event stream that says the rate changed before the infusion started is a clock problem, and it is indistinguishable from a device problem without a stated time source

Terminology. ISO/IEEE 11073-10101 nomenclature with the Rosetta Terminology Mapping value set, whose use is required in IHE DEV profiles per DEV TF-1 section 2.6, and UCUM for units. No specific MDC codes are asserted in this document. Codes look memorable and are easy to recall almost correctly, and an almost-correct code is worse than a missing one because it validates. Every code would be looked up in the published tables and agreed in writing before use.

What any such interface has to get right

These are not integration details. Each one is a way the record becomes wrong while every system reports success.

A device cloud API that handles those five is worth more than one that merely exists, and none of them is solved by choosing a transport.

The honest limits

  • Both sandboxes are test environments and behave like it. The data quality findings above are evidence about the sandboxes, not about Epic's or Oracle's production systems, and this document does not claim to know how a live instance behaves.
  • Sandbox CDS Hooks support is still unconfirmed at the vendor end. Our service is live and conformant and answers any client that calls it. Whether either vendor's sandbox will call out to a third-party service has not been tested, and no claim is made that it will.
  • Oracle Health and MEDITECH are named Da Vinci CRD contributors. Da Vinci CRD is built on CDS Hooks. That is a contribution to a standard and it is not verified production CDS Hooks support in either product. The distinction is deliberate.
  • Seven of eight screening classes have no source. Allergy matching is substring matching on product names, which is why a saturated screen prints a count instead of a list. Ingredient-level checking needs a maintained drug knowledge base and the socket for one is deliberately empty.
  • Production is walled at every vendor, by design. There is no self-service route to a real chart at any of the three, and this project does not seek one.

Security and governance

  • Authentication is a signed assertion, not a password. The private key stays server side and is never committed to source control.
  • Every sandbox used here contains synthetic patients only. Vendor terms prohibit identifiable information in a sandbox, and this project has no use for it.
  • All traffic is TLS 1.2 or higher.
  • Advisory only, human in the loop. Every write lands somewhere a clinician has to sign, adjust or discard.
  • The integration is developed in isolation from the public demonstration site, and reaches it only by a deliberate act.

What this is not

This is not a partnership with, an endorsement by, or a listed application of Epic, Oracle Health or MEDITECH. None of them certifies, endorses or verifies third-party applications, and Epic says so plainly. It is not a product, not a pilot, and not a medical device. It is a demonstration that a method already shown to work on a simulated encounter can be fed by a real chart, built so that a licensee can evaluate the integration path instead of being asked to take it on faith.

Patent and provenance

Patent pending. The method is the subject of US provisional patent applications filed in 2026. It continues one long thread of the same idea: real-time reasoning over a live physiological signal (a processed-EEG algorithm, 1986), population-scale medical-terminology reasoning (UMLS on the MediCompass platform, 2003), and now language-model reasoning over the live medication context.

For additional information, contact Daniel Pettus at dan@aimedagent.net.
AI MedAgent · aimedagent.net · Draft, not for distribution
Advisory only. Not a medical device.