Syncing Salesforce with a clinical EMR, without buying middleware

Bidirectional sync between Salesforce and an EMR does not require an integration platform. A stage-and-link pattern built natively in Apex handles delta sync, conflict resolution, and idempotent upserts.

A behavioral-health treatment network ran its business on two systems that refused to talk: Salesforce for admissions and relationships, and a clinical EMR for everything medical. Staff retyped patient and assessment data between them daily, with the error rates you would expect. Conventional wisdom said buy an integration platform. The license quote said otherwise.

The short answer

We built bidirectional sync natively, with a stage-and-link pattern:

  • Inbound (EMR to Salesforce): a scheduled job runs every fifteen minutes and asks the EMR’s API only for what changed since the last run. Each payload lands in a staging object verbatim. A linker step then matches staged rows to real records via external IDs and upserts. If anything fails, the staged record holds the evidence and the retry is one click.
  • Outbound (Salesforce to EMR): when a record reaches the right stage (or a user clicks the sync button), an async job posts it to the EMR’s endpoint, with the returned identifier stored for all future round-trips.

No middleware, no new monthly platform bill, no third operational surface to monitor. The whole integration deploys, versions, and tests like the rest of the org.

Conflict resolution you can defend in an audit

The genuinely hard question in bidirectional sync is never plumbing; it is who wins. Here, the answer was encoded per field in custom metadata: clinical fields defer to the EMR (the clinical source of truth), relationship and pipeline fields defer to Salesforce. Because that policy is configuration, a compliance reviewer can read it, and changing it is an admin edit with an audit trail rather than a code release.

Why staging earns its keep

Direct-to-object integration fails ugly: a malformed payload throws mid-update and leaves a record half-changed, with no trace of what arrived. Staging fails gracefully: the raw payload is preserved, the error is attached to it, the linker skips it, and everything else flows. In months of operation, every integration incident was diagnosed from staged rows alone, without vendor calls or packet captures.

When native beats middleware (and when it does not)

Native Apex integration is the right call when there is one or two integrations, the volumes fit comfortably in platform limits, and the team maintaining it lives in Salesforce. An iPaaS earns its license when integrations multiply across many systems, or transformation logic becomes a product of its own. The mistake is defaulting to the platform purchase before counting the integrations. One connection rarely justifies it; this one certainly did not.

What the client got

Double entry ended. Admissions saw clinical status without leaving Salesforce; clinicians saw referrals without leaving the EMR. The fifteen-minute cadence proved more than fresh enough for operations, and the absence of middleware meant the absence of a middleware bill, every month, forever.

Common questions

Do I need MuleSoft or an iPaaS to integrate Salesforce with an EMR?

Not necessarily. For a single high-value integration, a native Apex pattern (scheduled delta pulls, staging objects, external-ID upserts) delivers bidirectional sync with no middleware license, no extra vendor, and no new infrastructure to operate.

How do you handle conflicts when the same record changes in both systems?

Declare a source of truth per field, in configuration. In clinical integrations the EMR typically wins for clinical data and the CRM wins for relationship data. Encoding that per-field policy in custom metadata makes conflict resolution auditable and changeable without code.

What is a staging object pattern in Salesforce integration?

Inbound payloads land first in dedicated staging records, exactly as received. A second step links and upserts them into real objects via external IDs. Failures are visible and replayable, and bad data never half-updates a production record.