EHR Data Integration Guide | Rainfall Health

Integration Documentation

EHR Data Integration Guide

How Rainfall Health integrates with your EHR to ingest clinical data into a managed FHIR R4 store. Intended for customer integration teams, EHR vendors, and security and compliance reviewers.

Overview

Our integration ingests clinical data from your EHR through two complementary channels:

  1. Real-time, event-driven ingestion — triggered by HL7v2 ADT messages from your interface engine. When a relevant patient event occurs, we fetch that patient's clinical record over your EHR's FHIR R4 API and write it into our FHIR store.
  2. Bulk ingestion — initiated against your EHR's FHIR Bulk Data Access ($export) endpoint. We retrieve NDJSON files representing a population of patients and import them into the same FHIR store.

Both channels normalize identifiers, deduplicate against existing records, and record provenance for every write.

Standards we support

StandardUse
HL7v2 (ADT)Event triggers for real-time ingestion
FHIR R4Canonical data model for all ingested records
SMART on FHIR — Backend ServicesAuthentication to your EHR's FHIR API (client credentials with signed JWT assertion, per HL7 SMART App Launch)
FHIR Bulk Data Access (Flat FHIR)Population-level export from your EHR
FHIR ProvenanceAudit record written for every ingested resource

We sign client assertions using RS384 as required by the SMART Backend Services specification.

Data we ingest

For each patient triggered into the pipeline, we retrieve the following FHIR R4 resources from your EHR:

We also follow reference fields contained in those resources. If a referenced resource is not already in our FHIR store, we fetch it from your EHR so the record is internally consistent. Common follow-on resource types include Practitioner, Organization, Location, Medication, and DocumentReference.

The SMART scopes we request are limited to read-only system scopes:

system/Patient.read
system/Encounter.read
system/Observation.read
system/Condition.read
system/Medication.read
system/MedicationRequest.read
system/DocumentReference.read
system/Provenance.read

We do not request or use any write scopes against your EHR.

How real-time ingestion works

  1. Your interface engine sends an HL7v2 ADT message to our listener endpoint.
  2. We validate the message's trigger event against an allow-list configured for your tenant. Messages outside the allow-list are ignored.
  3. The raw HL7v2 message is archived in encrypted object storage for audit and reprocessing.
  4. The patient's MRN is extracted from the PID segment and queued for FHIR retrieval.
  5. We obtain a SMART access token from your EHR's authorization server and resolve the MRN to the EHR's FHIR Patient.id.
  6. We fetch the patient and their clinical resources, follow references, and write each resource into the FHIR store.
  7. After ingestion completes, we emit a structured completion event that downstream consumers (such as care-team attribution workflows) can subscribe to.

Messages that fail processing are routed to a dead-letter queue for inspection and replay.

Supported HL7v2 trigger events

The set of ADT trigger events that initiate ingestion is configured per tenant during onboarding. Typical configurations include patient registration, admission, discharge, transfer, and update events (for example, ADT^A04, ADT^A08). Triggers outside the configured set are acknowledged and discarded.

How bulk ingestion works

  1. We issue a Bulk Data $export request against your EHR (either system-level or scoped to a Group you provide).
  2. We poll the export job status URL returned by your EHR until the job completes.
  3. We download each NDJSON file the EHR exposes, store it in encrypted object storage, and stream-import the contents into the FHIR store.
  4. Each row in each NDJSON file is upserted into the FHIR store using the same safeguards described under Data integrity.

Bulk ingestion uses the same SMART Backend Services credentials as real-time ingestion.

Identifiers and patient matching

We support two ID modes, selectable per tenant:

In either mode, MRNs are never used directly as FHIR resource IDs.

Data integrity and idempotency

Provenance and audit

For every resource we successfully write, we also write a FHIR Provenance resource that records:

Raw inbound HL7v2 messages are retained in encrypted object storage with a content type of text/plain and a deterministic prefix, so the original message can always be retrieved to reconstruct ingestion history.

Security

Signing keys and JWKS

To authenticate to your EHR's FHIR API via SMART Backend Services, we sign each client-credential JWT assertion with a private RSA key held in our managed secrets store. Your EHR's authorization server validates that assertion by looking up the matching public key in our published JSON Web Key Set (JWKS).

The JWKS is served from a small, dedicated HTTPS service that is independent of the ingestion pipeline. You point your EHR at the JWKS URL once during onboarding; from that point forward your EHR fetches fresh public keys from us directly, and no manual key handoffs are required for routine rotations.

JWKS endpoints

URL patternPurpose
https://secure.rainfallhealth.com/.well-known/jwks.jsonDefault environment JWKS, used when a tenant is not in scope
https://secure.rainfallhealth.com/jwks/<tenant>.jsonTenant-specific JWKS — one public key set per integration

JWKS responses use the standard application/jwk-set+json media type and include cache hints so your EHR can revalidate efficiently:

Your EHR's actual JWKS URL is provided during onboarding.

Supported signing algorithms

Each JWK in the published set declares its alg, kid, modulus, and exponent. Your EHR selects the correct key for validation by matching the kid field in the JWT header against the keys in our JWKS.

Rotation policy

We follow an overlap-window rotation policy so rotations never interrupt ingestion or require coordinated downtime:

  1. A new RSA keypair (2048-bit) is generated. The private key is written to our managed secrets store; only the public key is ever exposed.
  2. The new public key is appended to the JWKS document. The JWKS now contains both the previous and the new keys, each identified by its own kid.
  3. We begin signing new client-credential assertions with the new kid immediately.
  4. The previous key remains published in the JWKS for a configurable grace period (default: 24 hours) so any in-flight assertions and any authorization-server-cached JWKS state continue to validate cleanly.
  5. After the grace period elapses, the previous key is pruned from the JWKS document. The corresponding private key version is retained in our secrets store for audit but is no longer used to sign new assertions.

Each key in the JWKS carries an issued-at timestamp (iat) that drives the pruning step. You can inspect the published JWKS at any time to see the active key set and their iat values.

For incidents where a key must be retired immediately (for example, suspected compromise), we can shorten the grace period to zero and prune the affected key on the next rotation. Your account team will notify you in writing if an emergency rotation is required for your tenant.

What this means for your integration

Tenant identifiers in JWKS URLs

Tenant slugs used in the JWKS URL match ^[a-z0-9][a-z0-9-]{0,62}$ — lowercase alphanumerics and hyphens, up to 63 characters. Your tenant slug is assigned during onboarding and will not change for the lifetime of the integration.

Compliance

The pipeline is designed to operate inside a HIPAA-aligned environment:

A copy of our current BAA, security questionnaire responses, and SOC 2 report is available on request from your account team.

What you need to provide

To complete an integration, you (or your EHR vendor) will provide:

  1. SMART Backend Services registration. A client_id, the token endpoint URL, and the registered public key (kid) corresponding to a private key we will hold in our secrets store.
  2. FHIR base URL. The R4 base URL for your EHR's FHIR API.
  3. Bulk export endpoint and scope. If you want bulk ingestion enabled, either confirmation that system-level $export is enabled, or the Group resource ID we should export.
  4. HL7v2 delivery method. How your interface engine will deliver ADT messages to our listener (typically a queue, HTTPS endpoint, or VPN-routed MLLP).
  5. ADT trigger allow-list. The list of ADT trigger events that should initiate real-time ingestion.
  6. Tenant identifier. A short, stable string we will associate with your data for partitioning and audit.

You do not need to provide network access from your environment into ours. All flows are outbound from our environment to yours.

Onboarding for Epic customers (Epic Showroom)

If your organization is an Epic customer, the recommended path to enable this integration is through Epic Showroom, Epic's marketplace for vendor-built FHIR and HL7v2 integrations. This section is written for Epic technical contacts — Client Systems Administrators (CSAs), Bridges analysts, interface engineers, and security reviewers.

The exact in-app navigation in Epic Showroom and Hyperspace varies by Epic release. The steps below describe the workflow at a level that applies to any current Epic version; your Epic representative can map them to the specific screens in your environment.

What you will accomplish in this section

  1. Locate and request the Rainfall Health Data Adapter listing in Epic Showroom.
  2. Trigger Epic's internal workflow to enable the integration for your organization.
  3. Register the SMART Backend Services client and our JWKS URL on the Epic side.
  4. (Optional) Configure the HL7v2 ADT outbound interface for real-time ingestion.
  5. Validate end-to-end against your Epic non-production environment.

Prerequisites

Before starting, please confirm with your Epic representative and our account team:

Step 1. Acquire the listing on Epic Showroom

  1. Sign in to Epic Showroom using your Epic UserWeb credentials. (Your Epic representative or our account team can provide the current Showroom URL and a direct link to our listing.)
  2. Search for Rainfall Health Data Adapter, or follow the direct listing link your account team provided.
  3. On the listing page, request the integration via the action your Showroom environment exposes (commonly labelled along the lines of Get this app or Add to environment). This submits a request to Epic to enable the integration for your organization and notifies us as the vendor.
  4. Track request status in Epic Showroom under your account's request list.

If you cannot find the listing, contact support@rainfallhealth.com and we will share a direct link.

Step 2. Install / enable the integration on your Epic environment

This integration does not require any binary or container to be deployed inside your Epic environment. The Showroom "install" consists of four configuration steps on the Epic side:

  1. Epic provisions a SMART Backend Services client scoped to your organization and your environment (non-prod first, then prod).
  2. Your team registers our public JWKS URL against that client in your Epic environment's vendor-services or client-registration screen.
  3. Your security stakeholder approves the requested scopes — the read-only system scopes listed in Data we ingest.
  4. Epic provides the FHIR R4 base URL that your environment exposes for our read calls.

The JWKS URL to register on the Epic side is your tenant-specific URL:

https://secure.rainfallhealth.com/jwks/<your-tenant-slug>.json

Your <your-tenant-slug> is assigned during onboarding. See Signing keys and JWKS for the full description of how we publish and rotate keys at this URL.

Step 3. Provide configuration back to us

Once Epic provisions the client, your team will send us the values Epic generated:

ItemSourceHow we use it
Epic-issued client IDProvided by Epic in step 2Stored in our managed secrets store; used as the iss and sub of every signed JWT assertion
Token endpoint URLProvided by Epic for your environmentUsed as the aud of every signed assertion and as the target of token requests
FHIR R4 base URLProvided by Epic for your environmentUsed as the base for all read calls
HL7v2 trigger allow-listYour Bridges or interface teamConfigured per tenant in our HL7v2 listener

Send these to support@rainfallhealth.com or to the secure-share location your account team has provided. We do not write credentials to source control, logs, or environment files in plaintext.

Step 4. (Optional) Configure HL7v2 ADT delivery

If you are enabling the real-time ingestion path:

  1. Engage your Bridges analyst to configure an outbound HL7v2 ADT interface targeting the listener endpoint we provided during onboarding.
  2. Common trigger events for ingestion are ADT^A04 (registration), ADT^A08 (update), and ADT^A28 (add person record). Confirm your final list with us so it matches the per-tenant allow-list configured on our side.
  3. Run end-to-end tests from your Epic non-production environment first. Do not connect production HL7v2 traffic until non-prod validation has passed.

Step 5. Validate

After SMART registration is complete (and the HL7v2 interface is in place, if applicable):

  1. Trigger a test event from your Epic non-production environment. A synthetic patient is recommended for the first run.
  2. Confirm with us that the patient header arrived and was written to the FHIR store.
  3. Inspect the structured completion event we publish to your episode-attribution-requested consumer for the per-resource counts.

We monitor the first 48 hours of ingestion closely and work with you in real time to resolve any errors. The most common first-run issues are scope mismatches and trigger-allow-list mismatches — both fixed without redeployment.

Moving to production

  1. Repeat steps 1–3 against your production Epic environment. Epic typically requires a separate SMART Backend Services registration for production.
  2. Provide the production client ID and production FHIR base URL to us.
  3. We swap the tenant configuration to production and notify you when the cutover is live.

Troubleshooting

SymptomLikely causeFirst check
invalid_client from Epic's token endpointJWKS URL not registered, or registered against the wrong environmentConfirm the JWKS URL registered in Epic is https://secure.rainfallhealth.com/jwks/<your-tenant-slug>.json and that it matches the Epic environment (non-prod vs. prod)
unauthorized_client / access_denied for a scopeA requested scope is not approved for your clientConfirm the scope list registered in Epic matches the one in Data we ingest
Patient fetch returns 404 from EpicMRN-to-FHIR-ID resolution failedConfirm the MRN sent in HL7v2 matches the identifier system Epic indexes for Patient
Epic reports no key found for kid but our JWKS shows the keyEpic's JWKS cache is staleAllow up to one hour for Epic's cache to refresh, or contact Epic to force a refresh

For any error not covered above, contact your account team or support@rainfallhealth.com with the UTC timestamp, the Epic environment name, and the full error payload.

Operational characteristics

Frequently asked questions

Do you write data back to our EHR?

No. We only request read scopes and never call write endpoints on your FHIR API.

Can you ingest a resource type that isn't in the default list?

Yes. The default set covers the most common clinical use cases; additional resource types can be enabled per tenant during onboarding.

What happens if our EHR returns a malformed FHIR resource?

We log the error, attempt a small set of well-known remediations (for example, filling a missing required code with a data-absent-reason), and either succeed with the remediation noted in Provenance or route the message to the dead-letter destination for review.

Do you store PHI outside our cloud region?

No. Data is stored in the region you select during onboarding and does not leave that region.

How are duplicates handled?

Every write is idempotent. The same source record processed twice produces a single stored resource with a single version.

Support

For integration questions, credential rotation, or to schedule onboarding, contact your account team or support@rainfallhealth.com.