Skip to content

eClinicalWorks Integration

The eClinicalWorks integration is a read-only FHIR R4 Bulk Data connection. It authenticates with SMART Backend Services (asymmetric RS384 private_key_jwt) and pulls one Dataset per FHIR resource. Clinical Datasets use $export; the Groups Dataset uses FHIR search. There is no user login.

The eClinicalWorks integration does not run through the Resplendent Sync Agent.

You need:

  • A practice FHIR R4 base URL (the SMART issuer), such as the eCW sandbox https://staging-fhir.ecwcloud.com/fhir/r4/FFBJCD.
  • A published Backend / Bulk API Connect app and its client id.
  • The JWKS kid that matches the public key hosted at https://app.resplendentdata.com/api/.well-known/jwks.json.
  • The RSA private key for that kid, stored only in an environment variable (default ECW_JWT_PRIVATE_KEY). Never paste a PEM into Resplendent, tickets, or git.
  • A FHIR Group id created in the practice’s EMR Registry and enabled for the Connect app. eClinicalWorks does not support Patient/$export or server-level $export for this integration.

The practice must activate its eClinicalWorks FHIR APIs and authorize the Group for the app. App publication and successful token issuance do not grant access to a practice or Group by themselves. The shared eCW sandbox also needs an app-enabled sandbox Group before it can return synthetic patient data.

The token URL is discovered from {issuer}/metadata?_format=json (oauth-uris.token). Do not hardcode a practice token host.

The integration never creates, updates, or deletes records in eClinicalWorks.

Connect eClinicalWorks to Resplendent Data

Section titled “Connect eClinicalWorks to Resplendent Data”
  1. In Resplendent Data, go to Settings → Integrations.
  2. Find eClinicalWorks and select Connect.
  3. Enter:
    • FHIR base URL: the practice issuer.
    • Client ID: the SMART Backend Services client id.
    • JWKS kid: the key id in your hosted JWKS document.
    • Private key secret: the environment variable name that holds the RSA PEM (not the PEM itself).
    • Group ID: the FHIR Group that the practice enabled for this app.
  4. Test and save the connection.
  5. Select the Datasets and columns you want to sync.

The connection test discovers the token URL, mints an RS384 client assertion (iss/sub = client id, aud = token URL, kid in the header), and searches the app-visible Groups until it finds the configured Group id. If the Group is missing, ask the practice or eClinicalWorks to enable it for the Connect app.

Token requests use least privilege. The Groups Dataset requests only system/Group.read; each clinical Dataset requests system/Group.read plus that Dataset’s system/{Resource}.read scope.

Local and sandbox development should use the eCW FHIR sandbox issuer above. Do not point exploratory syncs at a customer production practice.

Resplendent syncs typed Datasets rather than one giant $export dump. Each clinical Dataset kicks Group/{id}/$export with _type set to that resource so a 60-second job never drains the whole server.

Dataset FHIR resource Sync Why
Groups Group Snapshot Small administrative list from FHIR search. Needed to see Group ids. Bulk $export of a Group returns patient data, not the Group list.
Patients Patient Incremental meta.lastUpdated exists, but Bulk files have no stable row watermark across NDJSON files. History is a JSON cursor over export files.
Encounters Encounter Incremental Same Bulk file cursor.
Observations Observation Incremental Same Bulk file cursor. Often the largest file set.
Conditions Condition Incremental Same Bulk file cursor.
Procedures Procedure Incremental Same Bulk file cursor.
Medication Requests MedicationRequest Incremental Same Bulk file cursor.
Allergy Intolerances AllergyIntolerance Incremental Same Bulk file cursor.
Immunizations Immunization Incremental Same Bulk file cursor.
Diagnostic Reports DiagnosticReport Incremental Same Bulk file cursor.
Coverages Coverage Incremental Same Bulk file cursor.
Care Plans CarePlan Incremental Same Bulk file cursor.
Care Teams CareTeam Incremental Same Bulk file cursor.
Goals Goal Incremental Same Bulk file cursor.
Devices Device Incremental Same Bulk file cursor.
Practitioners Practitioner Incremental Same Bulk file cursor.
Practitioner Roles PractitionerRole Incremental Same Bulk file cursor.
Organizations Organization Incremental Same Bulk file cursor.
Locations Location Incremental Same Bulk file cursor.
  • Groups is a snapshot Dataset. initial_pull returns the current Group list. Later ticks replace parquet. Cadence is slow (1440 minutes default).
  • Clinical Datasets are incremental only in the crawl sense: initial_pull returns one configured batch (batch_pull_size, default 1000) from the current $export and stores the status URL plus NDJSON file/line offsets on crawler_step_info. get_old_rows continues that cursor. New Datasets default to 9999 additional history batches so a large export can finish. This is the same JSON-cursor style as HubSpot, not a third pager.
  • A full $export is not drained inside initial_pull. If the export is still assembling, the job retries on the next tick using the saved status URL. If eCW returns HTTP 200 OperationOutcome duplicate (“Bulk operation is already in progress for the group”) with no Content-Location, that is the same pending state: the job does not write an empty page or mark the crawler completed, and it retries the same Group $export on the next tick (or polls if a status URL exists).
  • get_updated_rows uses Bulk _since against meta.lastUpdated (with a short overlap) once history is no longer mid-cursor. _since does not emit deletes.
  • nightly_resync is on so hard deletes and missed Bulk files disappear after a wipe-and-recrawl. Resync still first-pages incremental Datasets; last_resync_timestamp is not a fetch-everything switch.

Nested FHIR elements are stored as JSON strings. Identity columns are id and lastUpdated.