Guide
How to test an EHR integration
An EHR integration is judged by what the record system ends up holding, so every case needs a second assertion read out of the chart. The method runs from the environment you are allowed to point at, through the identity events that create and merge patients, to a cutover rehearsal at production volume and the evidence it leaves.
- Written for
- For a CTO
- Last revised
- 10 September 2026
What are you actually testing when you integrate with an EHR?
The chart, one step past the interface. A message that satisfies every rule its specification states can still land on the wrong patient, arrive under an account nobody can trace, or sit in the record system as a second copy of a person who already existed. FHIR R4 disclaims exactly that reading of its own scope: conforming to the specification, it says, guarantees nothing about patient safety or data safety. So the conformance run and the integration test are two pieces of work, and only the second one asks what the record system now holds.
That gives each interaction three assertions rather than one. The first is on the instance you sent, which a validator settles against a profile. The second is on the reply, which is a code and a segment or a returned resource. The third is on the stored record, which is read out of the EHR by a query somebody has to be entitled to run. The mechanics of the first two are covered per protocol in the order of work for an HL7 v2 interface test and in what each FHIR conformance testing tool proves. This page is about the third one, and about the project shape around it.
Two properties of an EHR make the third assertion harder than it sounds. The record system is usually the receiver, so the behaviour under test is consumption rather than emission. And the record system is shared: your interface is one of several writing into the same chart, so a value that appears where you expected it is not automatically a value your interface put there. Both push the same conclusion. A test case needs a patient it owns, an identifier it controls, and a read path agreed before the run.
Which environment answers for the customer you are going live with?
The one they gave you, and the first job is to work out which kind of document you were handed. The kind element of a CapabilityStatement takes three values in R4, and they describe three different objects. Instance covers a solution already deployed and configured at one access point, and R4 names it as the basis on which a specific installation is tested, certified or commissioned. Capability covers a product in general, and R4 records that a developer may use such a statement as a marketing document. Requirements covers a system somebody wants, written to be measured against the proposals in a procurement.
An EHR vendor publishes the middle one. The customer's deployment serves the first, and serves it from its own address. R4 leans on that: retrieving the statement is the single interaction every server has to answer, and every other call on the endpoint is optional until the statement declares it.
Two elements inside the statement carry different weight, and the difference is
testable in a morning. The profile element under a resource gives the general
features supported for that resource type, which R4 describes as the superset
across the use cases the system implements. The supportedProfile element gives
what the system handles for one use case, and declaring a profile there brings
two obligations with it. Instances have to be marked with a profile assertion
naming what they conform to, and a server has to answer a search on the
_profile parameter for the profiles it declared. Both cost one request to
check, and a partner who declares profiles and meets neither has told you
something about the endpoint before the first clinical case runs.
What to establish about the environment before any case is written
- Fetch the CapabilityStatement from the customer endpoint. Do not accept a PDF in its place.
- Read
kind. Confirm it is instance. - Read
fhirVersion. Record the exact string. - List every declared interaction and search parameter. Call each one.
- Record every call that returns nothing. Send that list to the customer.
- For each
supportedProfile, search by_profile. Confirm the search answers. - Name the EHR release the endpoint runs on. Record it beside the results.
Test data is settled in the same conversation, because an EHR test environment that is refreshed from production carries the live record of every patient in the organisation. Fixtures for this work are generated against the profile the interface has to satisfy, and the patients in them exist only inside the test system. We sign a Business Associate Agreement before any engagement that touches PHI, and the questions a buyer usually asks next are answered in our written positions on protected health information. Generated patients also survive a change of rule, since regenerating them costs a run, while a set lifted out of a live system is frozen at the release and the site it came from.
Which identity events belong in the plan?
The ones that create, correct and join patients, because those are the events that decide where every later value lands. A clinical interface that is tested only on new patients is tested on the easiest hour of its life.
HL7 v2 puts the boundaries in the trigger event definitions. ADT^A01 is narrow: "an A01 event is intended to be used for 'Admitted' patients only. An A01 event is sent as a result of a patient undergoing the admission process which assigns the patient to a bed. It signals the beginning of a patient's stay in a healthcare facility." Registration without a bed is A04, and HL7 Table 0003 lists both alongside A05 pre-admit, A06 change an outpatient to an inpatient, A08 update patient information, A11 cancel admit or visit notification and A17 swap patients. Each of those describes something that happens to a patient record after it exists, and each is a case with a different expected end state in the chart.
| The event | What the chart has to show afterwards |
|---|---|
| A04, register a patient | One record, one identifier, and no second patient created by a later message about the same person |
| A08, update patient information | The corrected value in place, and the value it replaced still retrievable |
| A11, cancel admit or visit notification | The visit removed or voided, with the patient record itself intact |
| A17, swap patients | Each record still reachable under its own identifier, with the swap applied to every field it touches and to none it does not |
| A06, outpatient to inpatient | One continuous record rather than two encounters that do not know about each other |
The FHIR side has a matching mechanism and its status is worth knowing before you plan around it. The Patient resource is one of eleven resources marked Normative in R4, and the ANSI Patient Package that carries it also carries the LinkType value set and code system. The elements that express a link between patient records therefore sit inside balloted normative content, so a test written against them is testing something the specification has committed to keeping.
Cancellation and swap are where most integrations first show damage, and the damage is quiet. Nothing errors. A cancelled visit that was never voided leaves an encounter that later results attach themselves to, and a swap that the receiving system applied to one field and not another leaves two charts that each look plausible on their own. Neither is visible from the sending side, and neither is visible from a row count. They are visible from a read of both records.
In what order does the work run?
Nine steps. The order is set by which artefact the next step needs.
The sequence for one EHR integration
- List the interactions across the boundary. Give each one a direction and one owner on each side.
- Establish the environment. Complete the seven checks above and record the answers.
- Agree the read path. Name the query, the account and the person who runs it.
- Build the patient set. Give each test patient one identifier per system and record the mapping.
- Run the identity events. Assert on the chart after each one.
- Run the clinical payload. Assert on the instance, the reply and the stored value.
- Run the negative cases. Force each rejection the receiver can return.
- Read the audit record. Confirm the interface account is named on every write.
- Rehearse the cutover. Replay a day of traffic at production volume and keep the run.
Step 4 is the one that pays back later. An EHR integration involves at least two identifiers per person, one in your product and one in the record system, and often a third issued by an interface engine in between. A test patient with no recorded mapping is a test patient nobody can find again, so the failure it produced is unreproducible by the second week.
Step 8 exists because an interface writes as a machine. Audit controls at 45 CFR 164.312(b) is a required standard covering mechanisms that record and examine activity in systems holding electronic protected health information, and 45 CFR 164.308(a)(1)(ii)(D) obliges the entity to review what those mechanisms recorded. Neither provision names an event list, so the only testable question is a local one: does the write your interface just made appear in the record of activity, attributed to something a reviewer can identify. Run that check while the interface is new and the account is still being created, since after go-live the account is a production object nobody wants to experiment with.
Step 9 changes what a defect costs, and it surfaces a class of fault no single message ever shows: the queue that grows behind the interface engine when a day of traffic arrives at its real rate, and the second chart created when a retried A01 is not recognised as the message the record system already accepted. A fault of that kind found during a replay is a ticket with a week of slack behind it. The same fault found on the morning of the cutover is a decision taken under time pressure by people who cannot assess it, and the option they usually take is to proceed.
How is the stored record read back?
Three routes, and an EHR integration normally needs two of them.
The first is the application itself. Somebody with clinical rights opens the chart and reads the value. This is the route that proves the most, because it ends where a clinician's eyes end, and it is the route that scales worst.
The second is the record system's own API, which is available whenever the EHR serves FHIR. Writing over HL7 v2 and reading back over FHIR is the most useful pattern in this work: the write path and the read path are independent, so a value that survives both has passed through two independent implementations. Two US Core rules decide how to grade what comes back. A responder holding no value, and no known reason for the absence, has to omit the element from the query result. A requestor has to read that omission as data the responder does not hold. An absent element is therefore a result with a meaning, and the assertion has to separate three states: the expected value, a wrong value, and nothing at all.
Before relying on that route at all, find out which level of US Core the partner built. The guide names two: Profile Only Support, where "systems MAY support only the US Core Profiles to represent clinical information" with no expectation of implementing the RESTful interactions, and Profile Support with Interaction Support, where both are implemented. A partner at the first level holds conformant data your client cannot query, and the claim "we support US Core" covers either case. The regulated floor underneath sits at 45 CFR 170.215(a)(1), which adopts FHIR Release 4.0.1, and 45 CFR 170.215(b)(1)(ii), which names US Core STU 6.1.0. What a certified module owes a caller on top of that is set out in the certification criteria an EHR is tested against.
The third route is a data extract, and it belongs to migration work. It answers a different question, which is whether a whole population arrived intact, and it needs a comparison against a source that is about to be switched off. Testing a move of clinical records between systems covers that case.
What does the work leave behind?
Six records. Each is short, and each exists because somebody asks for it once the interface is carrying live traffic.
The environment sheet names the endpoint, the kind of CapabilityStatement it served, the FHIR version string, the EHR release and the date. It is the document that tells a later reader which system a result describes.
The patient identity map holds one row per test patient. Each row gives the identifier in every system that saw the patient. Keep it with the fixtures, not in a ticket.
The identity event log holds one row per event run. Each row names the trigger, the expected end state in the chart, and the state found. Cancellations and swaps get their own rows.
The chart evidence record holds the read-backs. Each row names the field, the query that read it, the expected value and the value found. Name the route used, because a value read through the API and a value read on screen are two separate observations.
The interface account record names the account each write was made under, and the entry that write produced in the record of activity. Attach the query that retrieved the entry.
The cutover rehearsal record holds the replay: the volume, the duration, the queue depth, the failures and the recovery. Date it against the build.
Those six belong to one interface. An estate with a dozen of them needs the register and the version matrix that sit above them, which is scoped as testing an exchange against the partners it runs with. The failures that live inside the record rather than at its boundary, from the audit trail to the code sets, are set out on testing an EHR or EMR platform.
What continues after the interface is live?
Regression against a partner you do not control, and, for a certified product, a reporting obligation with dates on it.
The engineering half is straightforward to schedule and easy to forget. An EHR upgrade changes the configuration behind the endpoint without touching your code, so the environment checks above belong in a recurring job, with the setup document as their baseline. Run them against the customer endpoint, compare against the environment sheet, and treat a difference as a release event.
The regulatory half applies when your product carries an ONC certification. section 170.405, Real World Testing, obliges a developer holding a Health IT Module certified to the criteria that section lists to test the interoperability of that module in use, in the setting the module is sold into. The dates around it are fixed. The annual plan reaches the ONC-Authorized Certification Body in time to be published on CHPL by 15 December, and the results report in time to be published by 15 March. The clock that matters to an integration team is shorter: section 170.405(b)(2)(i) gives a developer 30 days to report to its certification body any non-conformity with the full scope of a criterion that real world testing turns up. A defect discovered in a live customer interface can therefore start that clock, which is an argument for finding it during the rehearsal.
The near-term position is softer than the rule text, and it expires on a date already published. ONC is exercising enforcement discretion here: no annual plan was expected from a developer for the 2026 testing year, and for 2026 the expectation narrows to developers whose modules were certified to the (g)(7) through (10) criteria as of 31 August 2024, who were asked for a results report covering 2025 by March 2026. ONC states that the discretion holds until 31 December 2026, or until the Department of Health and Human Services finishes its deregulatory action, whichever arrives first. An integration programme scoped on the belief that the obligation has gone is scoped past that date.
Where does an EHR integration go wrong?
- The vendor's published capability document stands in for the customer's own endpoint, so the suite describes a product and the integration meets a configuration.
- The read path is never agreed, so every case ends at the acknowledgement and the chart is inspected once, by hand, on the day before go-live.
- Test patients have no recorded identifier mapping, so a defect found in week one cannot be reproduced in week three.
- Only new patients are exercised, so cancellation, correction and swap behaviour is first observed on real records.
- A cancelled visit is not voided in the record system, and later results attach themselves to an encounter that should not exist.
- The interface writes under a shared service account, so the record of activity names a machine and cannot answer which system made the change.
- A partner claims US Core support at profile level only, and the client is written against interactions the partner never implemented.
- An absent element is graded as a transport failure, although the guide requires a responder to omit it when the reason for absence is unknown.
- The cutover is rehearsed at test volume, so the first production-rate hour is also the first observation of queue depth and retry behaviour.
- The environment sheet is written once, and the customer's EHR upgrade six months later is discovered by a support ticket.
- The test EHR is refreshed from production so that the data "looks real", which puts live records into an environment whose controls were never designed to hold them.
The last one is the item that outlives the project. 45 CFR 164.312(e)(1) requires technical security measures against unauthorised access to electronic protected health information in transit, with integrity controls and encryption as addressable implementation specifications beneath it. A refreshed test copy inherits the obligation and rarely inherits the controls, and the decision that created it is usually taken by whoever needed data first, months before anyone asks who is accountable for it.
Clause numbers, quotations and dates on this page were taken from the primary texts of HL7 v2, FHIR R4 and US Core, the ONC Health IT Certification Program and the HIPAA Security Rule, each checked on 2 September 2026.
What do buyers ask about this?
- Can we test against the EHR vendor's sandbox instead of the customer's own instance?
- For a first pass, yes. For a go-live decision, no. R4 reserves the instance kind of CapabilityStatement for a solution already deployed and configured at one access point, and names that kind as the basis on which a specific installation is tested, certified or commissioned. The capability kind covers a product in general, and R4 says a vendor may use it as a marketing document. A sandbox answers for the software. Only the customer endpoint answers for the configuration your interface will meet.
- Our EHR partner says it supports US Core. What does that entitle us to?
- Less than it sounds, until they say which of two levels they built. US Core defines Profile Only Support, where systems support the profiles to represent clinical information with no expectation that the RESTful interactions are implemented, and Profile Support with Interaction Support, where both are implemented. A partner at the first level can hold conformant data your client cannot query. Ask which level, and ask for the official profile URLs declared in the server's supportedProfile element.
- An element we need comes back empty from the EHR. Is that a defect?
- Usually the opposite. Under US Core, a responder that holds no value and no known reason for its absence is required to leave the element out of the query result altogether, and the requesting system is required to read that omission as data the responder does not hold. So an empty answer is an answer. A client that raises an error on it holds the defect, and the case that catches it queries a deliberately sparse patient.
- Who has to sign a contract before we touch the customer's test EHR?
- Whoever will hold or move the data. 45 CFR 160.103 brings a person providing data transmission services with respect to PHI, where that access is required on a routine basis, inside the definition of a business associate, and the same paragraph extends it to a subcontractor who creates, receives, maintains or transmits PHI on a business associate's behalf. We sign a Business Associate Agreement before any engagement that touches PHI. Ask for the interface account to be issued in the customer's own directory, whoever the supplier is, so the access can be withdrawn without asking anyone.
Which standards does this touch?
Which product types does this apply to?
Which of our services test it?
What does validating your product actually involve?
Answer four questions about your markets, your product type and its integrations. You get the standards that reach you, the artefacts each one asks you to produce, and which of them a test supplier delivers.