Nobody could say where the kit was. Now the portal says it before anyone asks.

An at-home diagnostics platform ran a customer portal, Salesforce and a third-party logistics system that shared nothing. Staff created CRM records by hand, re-entered support actions into the portal, and opened a logistics dashboard to answer where a kit had got to. We built eight MuleSoft applications that keep all three in step.

Client
An at-home diagnostics platform
Sector
Digital Health
Platforms
MuleSoft · Salesforce · Logistics
Engagement
6-month engagement
Systems joined
A health portal, Salesforce, and a third-party logistics API.
Study
05 of five

One test kit, before and after

Struck through: the way it used to work
Customer registration in SalesforceManual — support staff created Account after portal sign-upAutomatic on portal registration via dh-customer-papi
Case-driven portal requestsManual — agent logged request in Salesforce, then repeated in portalAutomatic bulk PATCH from case sync, keyed on Journey_ID__c
Kit shipment visibilityLogistics dashboard only — no data in portal or SalesforceTracking number, carrier, status, delivery date synced to portal every 5 min
Journey data in SalesforceStale — no scheduled sync, portal and CRM diverged over timeScheduled sync via Journey_ID__c → Salesforce Cases + Accounts
Account safety on upsertNo protection — upsert could create wrong-type Account recordsRecordType filter blocks non-Member records before upsert
Case watermark accuracyLastModifiedDate — re-processed records changed by any updateIntegration_Timestamp__c — only updated when integration processes the case

Why it stayed that way.

Four constraints

The company's stack had grown around three independent systems. New customer registrations in the portal didn't automatically appear in Salesforce — support staff created them manually. When Salesforce agents flagged a case for a new kit, a blood draw reschedule, or a cancellation, someone had to translate that action back into the portal. Kit shipment status lived only in the logistics platform's dashboard; customers and support staff asking "where's my kit?" got no answer from either the portal or Salesforce.

  1. 01

    Customer registrations in the portal required manual Account and Case creation in Salesforce. Every new member was an entry that someone created twice.

  2. 02

    Case management was one-directional. Salesforce agents could log requests — new kit, blood draw reschedule, cancellation, telehealth follow-up — but those requests did not reach the portal automatically. A human had to transfer each one.

  3. 03

    Shipment tracking lived in the logistics platform. Neither the portal nor Salesforce could show customers their kit status, tracking number, or estimated delivery date without a separate dashboard login.

  4. 04

    Journey data in the portal and member data in Salesforce drifted apart over time. No scheduled sync, no external ID linkage, no single source of truth for member state.

What we ruled out first.

Four candidates, one of them chosen

The three systems involved — a proprietary health portal, Salesforce, and a third-party logistics API — have no native integration pathway. The portal's API uses API key authentication (X-Client-ID / X-Client-Secret) and exposes custom resources (journeys, orders, cases) that no off-the-shelf connector maps. The logistics platform uses Bearer token auth and returns structured shipment data with custom fields (kit_ids, tracking_status, carrier_code, service_code). Salesforce Person Accounts, custom fields (Portal_ID__c, Journey_ID__c, Integration_Timestamp__c), and the Member RecordType are all organisation-specific. No integration tool can connect these three systems without custom mapping — which is the integration, not a shortcut to it.

ZapierLow-code automation

DoesConnecting popular SaaS apps with standard triggers and actions

Rules it outNo support for custom API auth patterns; no Object Store for watermarks; no batch operations

WorkatoEnterprise automation

DoesPre-built connectors for popular business apps

Rules it outSame connector gap as Zapier for a custom portal API; no fine control over Salesforce Bulk API v2 behaviour

Point-to-point ApexNative Salesforce

DoesDirect Salesforce → external API calls

Rules it outSalesforce-only; cannot orchestrate Portal → Salesforce flows; tight coupling; no observability across systems

MuleSoftIntegration platform

DoesAPI-led architecture, DataWeave, Object Store, CloudHub

Why it wonHigher initial investment; right choice for multi-system orchestration with durable state and bidirectional flows

What we built instead.

Five flows across three systems

Eight MuleSoft applications — three System APIs, four Process APIs, and one batch job — created a complete integration layer across the health portal, Salesforce, and the logistics platform. The architecture follows API-led connectivity: each system API wraps one external system behind a stable interface, and each process API orchestrates business logic without touching external systems directly.

  1. Registration creates the Salesforce record

    Portal → Salesforce

    When a new member registers on the health portal, the dh-customer-papi process API receives the registration payload and creates a Salesforce Person Account under the "Member" RecordType — splitting the full name, mapping email, phone, gender, Portal_ID__c as external ID, subscription ID, and start date. If the registration includes a Journey ID, a linked Case is created simultaneously with Journey_ID__c as the external identifier. Both Salesforce record IDs are returned to the caller.

  2. Journeys sync to Salesforce on a schedule

    Portal → Salesforce

    The dh-journey-sync-papi scheduler fetches pending journeys from the portal (using portal user ID and journey ID as identifiers), queries Salesforce for existing Member Accounts by Portal_ID__c, and runs a RecordType filter to prevent accidental updates to non-Member records. Valid accounts are upserted with current journey data; Cases are upserted on Journey_ID__c. Portal IDs are zipped with Salesforce Account IDs after each batch to maintain the cross-system ID map.

  3. An agent flag becomes a portal request

    Salesforce → Portal

    Salesforce support agents flag cases using four boolean fields: New_Kit_Requested__c, New_Blood_Draw_Requested__c, Cancellation_Requested__c, New_Telehealth_Requested__c. The dh-case-sync-papi scheduler polls for cases where Integration_Timestamp__c exceeds the last-run timestamp (stored in Anypoint Object Store), transforms each flagged case into a structured request type ("new_kit", "reschedule_blood_draw", "cancellation", "follow_up"), and sends a bulk PATCH to the portal's journeys endpoint — up to 100 requests per batch. The Object Store watermark is updated after each successful run.

  4. A kit order becomes a shipment

    Portal → Logistics

    When a diagnostic kit order is created in the portal, dh-order-to-shipment-papi POSTs it to the logistics platform via Bearer token authentication — creating the outbound shipment record in the logistics system.

  5. Tracking comes back every five minutes

    Logistics → Portal

    The dh-order-status-sync-batch job fetches pending orders from the portal every five minutes, queries the logistics platform for each order's status and shipment details — tracking number, carrier code, service code, estimated delivery date, delivery timestamp, kit IDs — and PATCHes the order back to the portal. If an order has status updates but no shipments yet, the status is applied without shipment fields; once shipments exist, full tracking data is included.

What it is made ofTen components

Integration PlatformMuleSoft Anypoint Platform 4.x (8 applications)

DeploymentCloudHub

Salesforce ConnectivitySalesforce Connector — Bulk API v2, SOQL queries, upsert/create/update

Salesforce Data ModelPerson Accounts (Member RecordType), Cases (Journey_ID__c), Portal_ID__c external IDs, Integration_Timestamp__c

Health Portal ConnectivityHTTP (X-Client-ID / X-Client-Secret), Secure Properties

Logistics ConnectivityHTTP (Bearer token), SLP shipments/orders API

TransformationDataWeave 2.0

State ManagementAnypoint Object Store v2 (case sync watermark on Integration_Timestamp__c)

SecuritySecure Properties + Blowfish encryption

EnvironmentsMulti-env config (dev / qa / prod) via mule.env

Engineering notes.

The decisions that keep it exact

The watermark uses our timestamp, not the platform one

The case sync uses a dedicated custom field rather than Salesforce's LastModifiedDate to track which records have been processed. This was a deliberate v1.1.0 change noted in the flow code: LastModifiedDate can be updated by any system change, causing reprocessing of already-synced records. Integration_Timestamp__c is only updated when the integration itself processes a case, making the watermark exact.

The upsert checks the record type first

Before upserting accounts, the journey sync queries the Salesforce RecordType ID for "Member" accounts and filters the incoming payload to only include records where Portal_ID__c matches an existing Member-type account. This prevents the upsert from accidentally creating wrong-type Account records when a Portal_ID__c appears in the system but belongs to a different record type.

Lookups resolve in the system API, not the process layer

The Salesforce System API includes a generic lookup resolution layer: before upserting records, it detects lookup fields in the payload (identified by the presence of lookupTable and lookupField properties), queries Salesforce for the corresponding parent record IDs, and substitutes them into the payload. This keeps business logic out of the process layer and makes the SAPI reusable across any object with relational dependencies.

Every tracking field goes back to the portal

The order status batch fetches shipment-level detail for every shipment on the order: direction (inbound/outbound), estimated delivery date, tracking status, tracking number, tracking URL, carrier code, service code, delivery timestamp, and kit IDs. Each field is written back to the portal, making the full logistics picture visible without requiring direct access to the logistics platform.

What it settled.

Three figures, still true
0

manual Salesforce account creation — every portal registration creates the CRM record automatically

4

Request types an agent can raise in Salesforce and see land in the portal, unassisted

5 min

maximum shipment tracking lag — kit status, tracking URL, and estimated delivery date visible in portal within one polling cycle

What this one shows

Four things you can hold us to.

A case study is only worth reading if it generalises. These are the parts of this build that would show up again on yours.

  1. Different sync directions need different patternsPortal → Salesforce uses scheduled full-sync with RecordType filtering. Salesforce → Portal uses watermark-based incremental polling. Logistics → Portal uses a 5-minute batch. Each pattern matches its data type and latency requirement — applying the same pattern to all three would have made each one worse.
  2. External IDs are the architectural linchpinPortal_ID__c and Journey_ID__c make every upsert idempotent across all three sync patterns. Without them, every re-run creates duplicates; with them, every re-run is safe. External IDs are not a convenience — they are the safety property that makes scheduled sync reliable.
  3. Watermark field selection is a correctness question, not a convenienceThe v1.1.0 change from LastModifiedDate to Integration_Timestamp__c eliminated false re-triggers from unrelated field updates. A watermark on the wrong field produces duplicate work at best and inconsistent state at worst.
  4. Lookup resolution belongs in the system APIThe Salesforce SAPI's generic lookup resolver means process APIs pass field values, not Salesforce IDs. This keeps process APIs free from Salesforce-specific query logic and makes the SAPI reusable across any object with relational dependencies.