Orders were typed into NetSuite by hand. Now nobody is in the middle.

A specialist wholesale distributor ran Salesforce for the sale and NetSuite for everything after it, with nothing between them. Orders were re-keyed by the operations team; invoices and payments were invisible to sales without a second login. We built a MuleSoft API layer that moves customers, orders, invoices and payments both ways.

Client
A specialist wholesale distributor
Sector
Distribution
Platforms
MuleSoft · Salesforce · NetSuite
Engagement
5-month engagement
Sync surface
Platform Events one way, a 15-minute scheduler the other.
Study
04 of five

One order, before and after

Struck through: the way it used to work
Order fulfilment handoffManual re-entry by ops from Salesforce emailAutomatic on Salesforce Platform Event
Invoice visibilityNetSuite-only — required separate loginSynced to Salesforce every 15 minutes
Payment statusInvisible to sales team without NetSuite accessNear real-time via Platform Event
Customer account syncManual, inconsistentEvent-driven on Salesforce Account create/update
NetSuite order number in SFManual update after ops confirmsAsync writeback on NetSuite order creation
Error handlingSilent failures, discovered after the factHTML error email per failed record per scheduler run

Why it stayed that way.

Four constraints

Wholesale distributors run two essential systems that rarely talk to each other: Salesforce for the sales process, NetSuite for everything after the deal closes. When those systems are disconnected, the gap becomes a daily operational tax — paid in manual re-entry, stale data, and coordination overhead that scales with every additional order, customer, and invoice.

  1. 01

    Every Sales Order placed in Salesforce had to be manually re-entered into NetSuite by the operations team — creating a lag between deal close and fulfilment, and introducing a point of failure on every line item, discount, shipping address, and PO number.

  2. 02

    Invoice and payment status in NetSuite was invisible to the sales team without a separate NetSuite login. Account managers chasing payment status had to escalate to finance — information that already existed in a system they couldn't access.

  3. 03

    Customer account data — payment terms, credit limits, shipping preferences, tax IDs — was captured in Salesforce but not automatically reflected in NetSuite when accounts were created or updated.

  4. 04

    Shipment confirmations and NetSuite order numbers had no automated path back to Salesforce, leaving order records incomplete and requiring operations to manually close the loop.

What we ruled out first.

Four candidates, one of them chosen

NetSuite and Salesforce both have marketplace connectors and native integration tools. The challenge wasn't finding something with a NetSuite connector — it was finding one that could handle the actual requirements: real-time Platform Event-driven flows for orders and customers, 15-minute incremental polling for invoices and refunds with watermark-based pagination, bidirectional ID writeback, SOAP/XML operations for complex Sales Order creation, and per-flow error email notifications with failed record details. No off-the-shelf connector handles all of these in a maintainable, multi-environment integration with the reliability a production-grade financial sync requires.

ZapierNo-code automation

DoesSimple trigger-action flows with native connectors; fast setup for basic scenarios

Rules it outNo support for paginated batch polling, watermark-based incremental sync, or the complexity of SOAP/XML NetSuite Sales Order operations with conditional create-vs-update routing.

BoomiiPaaS

DoesHas NetSuite and Salesforce connectors; visual flow design; reasonable for standard object syncs

Rules it outLess flexible for custom Platform Event flows and conditional ID writeback logic; per-connection pricing grows with flow count; limited DataWeave-equivalent transformation power.

NetSuite SuiteApp for SalesforceNative connector

DoesOut-of-the-box mapping for standard Salesforce and NetSuite objects; no custom development for common fields

Rules it outFixed object and field mappings only. No support for custom Platform Events, custom financial objects, conditional writeback, or paginated incremental sync patterns.

MuleSoft Anypoint PlatformiPaaS / API-led

DoesFull connector library for Salesforce and NetSuite; DataWeave for field-level transformation; Object Store for watermark sync; per-flow error handling; CloudHub deployment

Why it wonHigher upfront investment in design and configuration vs. point-to-point tools; requires MuleSoft expertise to architect correctly.

What we built instead.

Four flows, two directions

CloudAlgo built a three-application MuleSoft integration following API-led connectivity principles. A Salesforce System API subscribes to Platform Events and orchestrates real-time flows from Salesforce to NetSuite, writing NetSuite record IDs back on success. A NetSuite System API wraps all create/update operations — SOAP/XML for complex Sales Order logic, REST for customers and payments. An orchestration application schedules incremental NetSuite → Salesforce syncs for invoices and refunds, and subscribes to payment Platform Events for near-real-time financial data.

  1. An account saved in Salesforce arrives in NetSuite

    Salesforce → NetSuite

    When a Salesforce Account is created or updated, an AccountsEvent__e Platform Event triggers the MuleSoft Salesforce SAPI. DataWeave transforms Salesforce Account fields — company name, payment terms, credit limit, tax ID/VAT, DUNS, shipping carrier, parent account — into a NetSuite Customer record. For new customers, the resulting NetSuite Customer ID is written back to the Salesforce Account. For existing customers, the record is updated by NetSuite ID.

  2. An order queued to fulfilment becomes a Sales Order

    Salesforce → NetSuite

    When a Salesforce Order is queued for fulfilment, a Create_NS_Order__e Platform Event fires. MuleSoft queries the Salesforce OrderItems — product SKUs, quantities, unit prices, discounts, expected ship dates, sequence names, quote line IDs — and transforms them into a NetSuite Sales Order via SOAP/XML. A DataWeave script handles country enum mapping, conditional null guards, and date format conversion. The resulting NetSuite Order ID and Number are written back to the Salesforce Order asynchronously.

  3. Invoices and refunds come back every fifteen minutes

    NetSuite → Salesforce

    Two schedulers run every 15 minutes — staggered by 7 minutes to prevent resource contention. Each fetches records modified since the last run, using a timestamp watermark persisted in Anypoint Object Store (minus a two-minute buffer for clock skew). Fetching is paginated: 50 records per request, with recursive sub-flow calls while hasMore == true. Records are posted to the Salesforce SAPI for upsert. Any HTTP 400 responses are collected, and a structured HTML error email is sent to the operations team.

  4. A payment posts against the invoice it belongs to

    Salesforce → NetSuite

    When a payment is recorded in Salesforce, an ABT_Payment_and_Refund_Event__e Platform Event carries the payment amount, NetSuite Account number, and NetSuite Invoice ID. MuleSoft transforms this into a NetSuite payment application — specifying the AR account, GL account, payment amount, and the exact invoice to apply it against. The payment is posted to NetSuite via REST, keeping accounts receivable in sync with Salesforce payment records without manual journal entries.

What it is made ofTen components

Integration PlatformMuleSoft Anypoint Platform 4.x

Salesforce ConnectivitySalesforce Connector (Platform Events, SOQL, Update)

NetSuite ConnectivityNetSuite Connector (SOAP/XML WebServices + REST)

TransformationDataWeave 2.0

State ManagementAnypoint Object Store v2 (sync watermarks)

DeploymentCloudHub

Error NotificationsSMTP (structured HTML email via DataWeave template)

SecuritySecure Properties + Blowfish encryption

TestingMUnit (MuleSoft unit testing framework)

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

Engineering notes.

The decisions that keep it idempotent

Platform Events are the bus, not direct callouts

All real-time flows are triggered by Salesforce Platform Events, not by direct cross-system API calls. This decouples Salesforce from NetSuite response times — a Salesforce Order save doesn't wait for a NetSuite SOAP call to complete. Platform Events provide built-in replay, retry, and delivery guarantees that a synchronous callout cannot.

A watermark, so the scheduler never re-reads

The invoice and refund schedulers use Anypoint Object Store to persist the last-run timestamp, minus a two-minute buffer to handle clock skew. Each run fetches only records modified since that timestamp — avoiding full-table scans, preventing duplicates, and handling API rate limits. The watermark can be reset to a configured default date via a config flag without code changes.

SOAP for the Sales Order, on purpose

The Sales Order create and update operations use NetSuite's SOAP WebServices API, not REST. DataWeave generates the full XML structure — including conditional field inclusion via null guards, country enum mapping via string camelize functions, date format conversion, and item list construction. This path was chosen deliberately: NetSuite's SOAP API supports field-level conditional inclusion and complex transactional object types that its REST API does not yet fully expose.

The IDs point both ways

Every flow that creates a record in one system writes the resulting ID back to the other. NetSuite Customer IDs and Order Numbers are written to Salesforce fields via Salesforce Connector update. Salesforce Account and Order Line IDs are embedded in the NetSuite payload as custom fields. This mutual reference pattern makes the integration idempotent: an update flow checks whether a NetSuite ID already exists on the Salesforce record before deciding to create or update.

What it settled.

Three figures, still true
0

Manual order re-entry — every Salesforce order creates a NetSuite Sales Order automatically

15 min

Invoice and refund sync cadence — NetSuite financial data reaches Salesforce in near real-time

4

Bidirectional sync flows automated — customers, orders, invoices, and payments

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. API-led architecture scales when requirements grow.A point-to-point Apex callout to NetSuite would have worked for the first flow. By the fourth, four separate maintenance surfaces, four separate error patterns, and no shared observability. The three-application API-led structure meant each new flow was a new sub-flow in the orchestration layer — not a new integration to build from scratch.
  2. Real-time and scheduled patterns solve different problems.Order and customer sync are event-driven because the business cannot wait for a scheduler. Invoice sync runs on a schedule because pulling NetSuite transactional data in batch is more reliable than expecting real-time triggers from the ERP side. Matching the integration pattern to the data type and latency requirement is an architectural decision, not a default.
  3. Incremental sync requires state management.A naive sync fetches all records on every run. An incremental sync persists a watermark and only fetches what changed. The difference is between a 15-minute scheduler that runs reliably and one that hits NetSuite API limits, overlaps with the previous run, and creates duplicate records. Anypoint Object Store makes that state persisted, configurable, and observable.
  4. Error visibility is part of the integration contract.Automated HTML error emails with failed record details aren't a feature — they're a reliability guarantee. Without them, a batch failure in an invoice sync is invisible until a sales rep notices a missing record two days later. With them, operations sees the failure within 15 minutes, with enough detail to diagnose the root cause without opening a dashboard.