Integration Documentation
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.
Our integration ingests clinical data from your EHR through two complementary channels:
$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.
| Standard | Use |
|---|---|
| HL7v2 (ADT) | Event triggers for real-time ingestion |
| FHIR R4 | Canonical data model for all ingested records |
| SMART on FHIR — Backend Services | Authentication 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 Provenance | Audit record written for every ingested resource |
We sign client assertions using RS384 as required by the SMART Backend Services specification.
For each patient triggered into the pipeline, we retrieve the following FHIR R4 resources from your EHR:
PatientEncounterObservationConditionMedicationRequestWe 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.
PID segment and queued for FHIR retrieval.Patient.id.Messages that fail processing are routed to a dead-letter queue for inspection and replay.
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.
$export request against your EHR (either system-level or scoped to a Group you provide).Bulk ingestion uses the same SMART Backend Services credentials as real-time ingestion.
We support two ID modes, selectable per tenant:
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.In either mode, MRNs are never used directly as FHIR resource IDs.
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.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.For every resource we successfully write, we also write a FHIR Provenance resource that records:
resourceType/id)recorded)agent.who.display)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.
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.
| URL pattern | Purpose |
|---|---|
https://secure.rainfallhealth.com/.well-known/jwks.json | Default environment JWKS, used when a tenant is not in scope |
https://secure.rainfallhealth.com/jwks/<tenant>.json | Tenant-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:
Cache-Control: public, max-age=300ETag: "<version>" — supports conditional requests with If-None-Match and returns HTTP 304 when unchangedYour EHR's actual JWKS URL is provided during onboarding.
RS256RS384 — the algorithm currently used for all SMART Backend Services assertions we issue, per the SMART App Launch specificationEach 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.
We follow an overlap-window rotation policy so rotations never interrupt ingestion or require coordinated downtime:
kid.kid immediately.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.
kid values in successive JWKS responses tells you exactly what changed and when.kid used, and rotation events are logged with the resulting active kid and key count.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.
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.
To complete an integration, you (or your EHR vendor) will provide:
client_id, the token endpoint URL, and the registered public key (kid) corresponding to a private key we will hold in our secrets store.$export is enabled, or the Group resource ID we should export.You do not need to provide network access from your environment into ours. All flows are outbound from our environment to yours.
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.
Before starting, please confirm with your Epic representative and our account team:
If you cannot find the listing, contact support@rainfallhealth.com and we will share a direct link.
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:
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.
Once Epic provisions the client, your team will send us the values Epic generated:
| Item | Source | How we use it |
|---|---|---|
| Epic-issued client ID | Provided by Epic in step 2 | Stored in our managed secrets store; used as the iss and sub of every signed JWT assertion |
| Token endpoint URL | Provided by Epic for your environment | Used as the aud of every signed assertion and as the target of token requests |
| FHIR R4 base URL | Provided by Epic for your environment | Used as the base for all read calls |
| HL7v2 trigger allow-list | Your Bridges or interface team | Configured 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.
If you are enabling the real-time ingestion path:
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.After SMART registration is complete (and the HL7v2 interface is in place, if applicable):
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.
| Symptom | Likely cause | First check |
|---|---|---|
invalid_client from Epic's token endpoint | JWKS URL not registered, or registered against the wrong environment | Confirm 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 scope | A requested scope is not approved for your client | Confirm the scope list registered in Epic matches the one in Data we ingest |
| Patient fetch returns 404 from Epic | MRN-to-FHIR-ID resolution failed | Confirm 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 key | Epic's JWKS cache is stale | Allow 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.
No. We only request read scopes and never call write endpoints on your FHIR API.
Yes. The default set covers the most common clinical use cases; additional resource types can be enabled per tenant during onboarding.
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.
No. Data is stored in the region you select during onboarding and does not leave that region.
Every write is idempotent. The same source record processed twice produces a single stored resource with a single version.
For integration questions, credential rotation, or to schedule onboarding, contact your account team or support@rainfallhealth.com.