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.

Document version: integration-guide v1.

01

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.

02

Standards we support

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

03

Data we ingest

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

  • Patient
  • Encounter
  • Observation
  • Condition
  • MedicationRequest

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:

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

04

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.

05

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.

06

Identifiers and patient matching

We support two ID modes, selectable per tenant:

  • Source ID passthrough (default). Each FHIR resource is stored using a deterministic identifier derived from the EHR's resource ID. EHR IDs that do not satisfy the FHIR id syntax (≤64 characters, restricted character set) are deterministically hashed to a valid FHIR ID. The same EHR ID always maps to the same internal ID.
  • Crosswalked IDs. We maintain a private mapping of EHR identifier → internal identifier so that internal IDs are opaque and stable independent of the source ID. Recommended when you need to ingest from multiple source systems into a single tenant.

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

07

Data integrity and idempotency

  • Idempotent writes. Before writing a resource we fetch the current version from the FHIR store and compare its canonical content (excluding server-managed fields such as meta.versionId, meta.lastUpdated, and narrative text). If the incoming resource is byte-equivalent to what is already stored, we skip the write. Replaying the same data does not create version churn.
  • Reference integrity. Resources are not written until referenced dependencies have been resolved. Missing references trigger a targeted fetch from your EHR before the dependent resource is committed.
  • Automatic remediation. A small set of well-known FHIR validation errors (such as a Condition or Observation missing a required code) are remediated with a data-absent-reason placeholder so ingestion is not blocked by upstream data-quality gaps. All remediations are captured in the Provenance record for the affected resource.
  • Dead-letter handling. HL7v2 messages and FHIR ingestion events that fail after retries are routed to a dead-letter destination for operator review.

08

Provenance and audit

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

  • The target resource (resourceType/id)
  • The wall-clock timestamp of the write (recorded)
  • The tenant on whose behalf the write was made (agent.who.display)
  • The role of the writer (source-system)

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.

09

Security

  • Transport. All EHR API calls use HTTPS. Tokens are obtained via the SMART Backend Services flow using a signed JWT client assertion (RS384). We never send your private key, and your EHR never sees ours.
  • Credential storage. SMART client credentials, signing keys, and key identifiers are stored in a managed secrets store and resolved at runtime. Credentials are never written to logs, source control, or environment files in plaintext.
  • At-rest encryption. Both the FHIR store and the object-storage buckets used for HL7v2 archives and bulk NDJSON staging are encrypted at rest using the cloud provider's managed encryption.
  • Tenant isolation. Every record we ingest carries a tenant identifier and is partitioned in storage by that tenant. Cross-tenant reads are not possible.
  • Least privilege. The SMART scopes we request are read-only. We do not request scopes we do not need, and we do not write back to your EHR.
  • Token lifetime. Access tokens are cached in memory for the duration of their issued lifetime (typically five minutes) and refreshed on demand. They are never persisted to disk.

10

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

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

  • Cache-Control: public, max-age=300
  • ETag: "<version>" — supports conditional requests with If-None-Match and returns HTTP 304 when unchanged

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

Supported signing algorithms

  • RS256
  • RS384 — the algorithm currently used for all SMART Backend Services assertions we issue, per the SMART App Launch specification

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

  • You configure the JWKS URL once. When we rotate, your EHR fetches new keys automatically — no coordinated change windows.
  • You can verify rotations. Comparing the kid values in successive JWKS responses tells you exactly what changed and when.
  • Private keys never leave our managed secrets store. Only public keys are ever exposed, and the JWKS service has read-only access to the public-key bucket — it cannot read, write, or even enumerate private key material.
  • Rotation is observable. Every signing operation records the kid used, and rotation events are logged with the resulting active kid and key count.

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.

11

Compliance

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

  • All managed services used (FHIR store, object storage, message queues, secrets storage) are covered under our cloud provider's Business Associate Agreement.
  • All PHI in transit is encrypted using TLS 1.2 or higher.
  • All PHI at rest is encrypted using AES-256 or stronger.
  • Access to production credentials and infrastructure follows least-privilege IAM controls with auditable access logs.

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

12

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.

13

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.

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:

  • A live Epic environment with Interconnect (FHIR R4) enabled.
  • Bridges licensed and configured, if you intend to use the real-time HL7v2-triggered ingestion path.
  • An Epic UserWeb account with access to Epic Showroom.
  • An identified internal owner for the integration — typically a CSA, Bridges analyst, or interface engineer.
  • A security stakeholder authorized to approve a new SMART Backend Services client and review the read-only scopes listed in Data we ingest.

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 info@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:

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:

Send these to info@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

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

14

Operational characteristics

  • Throughput. Real-time ingestion is horizontally scaled per message; each HL7v2 trigger is processed independently. Bulk ingestion throughput is bounded by your EHR's export performance and our import concurrency.
  • Latency. End-to-end latency from HL7v2 receipt to a queryable FHIR resource is typically under one minute for the patient header, with additional resources arriving as they are paginated from your EHR.
  • Retries. Transient errors (network, throttling) are retried with exponential backoff. Permanent errors are routed to the dead-letter destination after the retry budget is exhausted.
  • Replay. Both HL7v2 messages and bulk NDJSON files are retained in object storage so any ingestion can be replayed without re-exporting from your EHR.

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 info@rainfallhealth.com.

To report a compliance concern anonymously, please Click Here.

Document version: integration-guide v1.