MuleSoft System and Process APIs that connect JD Edwards to Salesforce B2B Commerce and Sales Cloud. JDE items become storefront products, account and contract pricing from JDE reaches the right buyer groups, JDE customers become buyer accounts, and carts go to the territory rep as a lead (Phase 1) or become JDE sales orders with status and invoices back to the buyer (Phase 2). Compares Direct API, scheduled sync and a recommended hybrid on the same System APIs, with idempotent orders, exception queues and a source-of-truth matrix per object.
Tags: jd-edwards, erp, salesforce-commerce, b2b-commerce, sales-cloud, order-to-cash, pricing, api-led, manufacturing, life-sciences
JD Edwards to Salesforce B2B Commerce: Catalog, Account Pricing and Order-to-Cash
Deliverable: A set of MuleSoft System and Process APIs, run in real time and on schedules, that connect JD Edwards to Salesforce B2B Commerce and Sales Cloud. JDE items become storefront products, account-specific JDE pricing reaches the right buyers, JDE customers become buyer accounts, and carts either go to a sales rep as a lead (Phase 1) or become JDE sales orders with status and invoices back to the buyer (Phase 2). Every flow has one agreed source, an idempotency key, and an owner for its failures.
Objective: Most JDE shops that add a B2B storefront start with custom code: scheduled jobs push items to the website, pricing is fetched from JDE and cached, and new accounts are keyed into JDE and Salesforce by hand. That works for one storefront. It breaks when a second site, region or ERP is added, when prices on the site drift from the invoice, or when the one person who wrote the jobs is unavailable. Working looks like this: JDE stays the system of record for items, pricing, customers, orders and invoicing; guests browse without prices; authenticated buyers see only their entitled catalog and contract prices; and an order is never lost, duplicated or booked at a price the buyer didn't see.
What's Getting Built
All of these are Mule applications on Anypoint Platform. The same System APIs serve the real-time and scheduled paths (see Choose your path), so no flow talks to JDE or Salesforce except through them.
- JDE System API (System API) — wraps JDE AIS / Orchestrator REST and hides JDE codes such as address book numbers, branch/plant, price groups and UOMs ·
GET /items?changedSince, GET /items/{itemNumber}, GET /customers/{abNumber}, GET /prices?customer&items, GET /inventory?items&branch, POST /sales-orders, GET /sales-orders/{orderNumber}, GET /invoices?customer
- Commerce System API (System API) — B2B Commerce products, variations, entitlement policies, buyer groups, price books and order summaries ·
PUT /products/{sku}, PUT /pricebooks/{id}/entries, PUT /buyer-groups/{id}/members, PUT /buyer-accounts/{externalId}, PATCH /order-summaries/{id}
- CRM System API (System API) — Sales Cloud accounts, contacts, ship-to and bill-to addresses, leads and cases ·
PUT /accounts/{externalId}, PUT /contacts/{externalId}, POST /leads, POST /cases
- Catalog Sync (Process API) — JDE item to Commerce product, with variant mapping, categories and entitlement ·
POST /catalog/sync, GET /catalog/sync/{runId}
- Pricing Sync (Process API) — JDE base, customer and contract pricing into Commerce price books, plus a live price check at checkout ·
POST /pricing/sync, POST /pricing/quote
- Account Sync (Process API) — JDE customer, ship-to and bill-to into Sales Cloud and Commerce buyer accounts, buyer groups and territory ·
POST /accounts/sync, POST /accounts/{id}/address-change
- Order Handoff (Process API) — Phase 1 cart to territory-routed lead; Phase 2 checkout to JDE sales order with status and invoices back ·
POST /orders, GET /orders/{id}/status, POST /orders/{id}/resubmit
- Integration Exception Queue (Anypoint MQ) — invalid, failed and duplicate messages with a dead-letter queue and a named owner per flow ·
PUBLISH integration.exceptions, PUBLISH orders.retry
Out of scope: product information management (PIM), CPQ and configurable products, punch-out, and multi-currency or multi-region pricing.
Prerequisites
- Anypoint Platform with CloudHub 2.0 (a Private Space for the VPN to JDE) or Runtime Fabric next to JDE, Anypoint MQ, and Anypoint Monitoring.
- A JDE current-state map: JDE version and tools release, hosting (on-premises, hosted or Oracle Cloud), whether AIS Server and Orchestrator are licensed and running, and every existing custom interface with its schedule. No estimate holds until this exists.
- Cloud-to-JDE connectivity: a VPN or private link, firewall rules, and a named approver. This usually takes longer than the build.
- A JDE integration user with role-based access to only the applications and business functions used (item master, price adjustments, address book, sales order entry, invoices).
- Salesforce B2B Commerce with a Commerce store, an integration user, and a connected app using OAuth 2.0 client credentials.
- Volumes at launch and at peak: orders per day, SKUs and variants, customers and ship-tos, price lists and adjustments, quotes and growth. They decide which flows run in real time and which run on a schedule.
- SLAs: order confirmation time, price accuracy, inventory accuracy, and an agreed staleness for pricing. Staleness is the single most important number in this design.
- Sales operations owners for territory rules, lead routing and account-team handoffs.
- Agreed product vs variant model (for example, a product with size or pack variants). It decides whether one JDE item is one Commerce product or one variation.
Choose your path
Every path is MuleSoft and uses the same JDE, Commerce and CRM System APIs. Only the way the Process APIs are triggered changes, so you can move a flow from one path to another without rebuilding a connector.
- Direct API (real time) — Commerce calls the Process APIs on demand (through the Commerce integration framework or Apex callouts to a Mule endpoint), and the Process API calls the JDE System API synchronously. Choose this for flows where the buyer is waiting and the answer must be current: live price at checkout, inventory, order placement and order status. JDE AIS has to be sized for storefront traffic, so put caching (Object Store) and rate limits (API Manager policies) on the JDE System API.
- Scheduled sync — A Mule Scheduler triggers the Process API, which reads changes from the JDE System API since a watermark kept in Object Store, processes them with Mule Batch, and upserts through the Commerce and CRM System APIs. Choose this for bulk data that can be minutes or hours old: catalog, price books, accounts and invoices. It keeps load off JDE during the day and makes each run a replayable unit with a run ID.
- Hybrid (recommended) — Scheduled sync for catalog, pricing, accounts and invoices. Direct API for the live price check, inventory, orders and status. This is the split in the table in step 1.
Phase 1 can run entirely on scheduled sync plus one real-time call (cart to lead). Phase 2 adds the real-time order, status and price-check flows on the same System APIs.
Architecture
1. Agree the system of source for every object
Fill this table in before building. The defaults below fit most JDE-led B2B storefronts.
| Object |
Source |
Flow |
Phase 1 |
Phase 2 |
| Products and variants |
JDE item master |
JDE → Commerce |
Scheduled sync, daily |
Scheduled sync, every few hours |
| Customers and addresses |
JDE address book |
JDE → Sales Cloud → Commerce |
Scheduled sync, plus real-time on demand |
Scheduled sync plus real-time on change |
| Account and contract pricing |
JDE pricing |
JDE → Commerce |
Scheduled sync, agreed staleness |
Scheduled sync plus real-time price check at checkout |
| Inventory |
JDE by branch/plant |
JDE → Commerce |
Hidden or "available / call" |
Direct API, cached |
| Orders |
Commerce |
Commerce → JDE |
Direct API: cart to lead, no JDE write |
Direct API with retry |
| Order status and tracking |
JDE |
JDE → Commerce |
Not applicable |
Scheduled poll of JDE status |
| Invoices |
JDE AR |
JDE → Commerce |
Not applicable |
Scheduled sync, or real-time on request |
| Tax |
Tax engine |
Tax engine ⇄ Commerce |
Not applicable |
Real-time at checkout |
- Keep pricing in JDE. B2B prices are negotiated per customer and tied to invoicing and AR. Maintaining them in Commerce as well means two price masters and invoice disputes.
- One external ID per record, end to end. JDE item number → Commerce SKU, JDE address book number → Salesforce account external ID, Commerce order ID → JDE customer reference. Matching on names creates duplicates.
2. Catalog sync: JDE items to Commerce products
- Pull items changed since the last run from the item master (and branch/plant item records for stocking and availability) and upsert Commerce products and variations on the JDE item number.
- Only publish items that are flagged sellable on the web, have a price, and have an entitlement. Make missing price or entitlement a validation error sent to the exception queue, not a silent filter, or products just vanish.
- Marketing content (descriptions, images, documents) usually doesn't live in JDE. Take it from wherever it's managed and join it on the same item number.
3. Pricing sync: JDE prices to Commerce price books
- Map JDE customer price groups and contract pricing to Commerce price books assigned to buyer groups. Guests get no price book. Authenticated buyers see only their own.
- JDE advanced pricing (adjustments, tiers, schedules) rarely maps one to one. Resolve it in the Process API to a net price per customer group and item, and store the JDE adjustment reference so a price can be traced back.
- Agree the staleness (for example, a JDE price change shows on the site by the next morning). If same-hour accuracy is needed, call
POST /pricing/quote against JDE at checkout. If JDE doesn't answer, use the synced price and mark the order "price to be confirmed".
- Stamp each sync run with a price book version ID, and carry it on carts, leads and orders.
4. Account sync: JDE customers to buyer accounts
- Credit and onboarding approval stays in JDE. Once the address book record, ship-to and bill-to exist, the sync creates or updates the Sales Cloud account and contacts, the Commerce buyer account, buyer group membership and territory. That removes the re-keying without removing the credit check.
- Match on the JDE address book number first and normalized name plus address second. Ambiguous matches go to sales operations instead of creating new accounts.
- Address changes made by buyers go to a case for approval before they're written to JDE, because ship-to changes affect tax and fulfillment.
5. Phase 1: cart to rep without a JDE order write
- The cart becomes a Sales Cloud lead or opportunity with the cart lines and price book version attached, routed by territory to the account's rep. The rep confirms and the order is entered into JDE as it is today.
- Controls: one lead per cart (idempotency key on the cart ID), the price book version on the lead so the rep sees what the buyer saw, and an SLA timer for first contact.
- Reconciliation: a daily report matches submitted carts to JDE orders using the cart ID entered in a JDE reference field. That turns manual effort into a measured number, which becomes the business case for Phase 2.
6. Phase 2: order-to-cash through JDE
- Checkout calls
POST /sales-orders. The JDE System API runs an Orchestration over sales order entry, returns the JDE order number, and Commerce shows it to the buyer.
- Status flows back as the order moves through JDE status codes (entered, released, picked, shipped with tracking, invoiced) and updates the Commerce order summary.
- Invoices are pulled from JDE AR so buyers can view and pay them in the account portal.
- Confirm before sizing Phase 2: tax engine, payment (cards vs PO and net terms), carriers and any WMS.
7. Failure handling
- JDE unavailable: accept the order, show "received, confirmation to follow", queue it and retry with backoff. Never show a failure to a buyer who has already submitted a PO or paid.
- Invalid data (unknown item, closed or credit-held account, price outside tolerance) goes to the exception queue with the reason and the owning team. It's never dropped or retried in a loop.
- Duplicates and reorders: the Commerce order ID is the idempotency key, stored in JDE. A retry with the same key returns the existing JDE order. A deliberate reorder gets a new key.
- Ownership: one named owner per flow (IT for connectivity, customer service for orders, sales operations for accounts, product team for catalog), with alerts routed to them, not a shared inbox.
8. Security and compliance
- OAuth 2.0 client credentials to Salesforce, and token-based auth to JDE AIS, with one integration user per system and least-privilege roles.
- JDE is reached only over the VPN or private link, using TLS 1.2 or later. It is never exposed to the internet.
- API Manager enforces client ID and OAuth policies on every Process API Commerce calls, and only Process APIs may call the System APIs.
- Secrets live in Anypoint Secrets Manager or encrypted secure properties, never in code.
- Log every write with correlation ID, source, target and outcome. Never log payment details or full contact records.
- If cards are taken in Phase 2, use the payment gateway's hosted fields so card data never enters a Mule application.
Sample payload — Phase 2 order into the JDE System API
{
"commerceOrderId": "{commerceOrderId}",
"idempotencyKey": "{commerceOrderId}",
"soldTo": "{jdeAddressBookNumber}",
"shipTo": "{jdeShipToNumber}",
"customerPo": "{buyerPoNumber}",
"branchPlant": "{branchPlant}",
"orderType": "{jdeOrderType}",
"priceBookVersion": "{priceSyncRunId}",
"lines": [
{ "itemNumber": "{jdeItemNumber}", "quantity": 2, "unitPrice": 0.00, "uom": "EA" }
]
}
Deterministic checks
POST /sales-orders returns 201 with a JDE order number. A replay with the same idempotencyKey returns 200 with the same order number and never creates a second order.
- A Phase 1 cart submission creates exactly one lead, assigned to the territory owner for the buyer's account, with cart lines and price book version attached.
- A guest session never receives a price. An authenticated buyer only receives prices from their own buyer group's price book.
- A line whose price differs from JDE by more than the agreed tolerance is rejected to the exception queue with the reason, not sent to JDE.
- Catalog, account and order syncs upsert on the JDE external ID and never insert when a match exists.
- Every flow has automated tests for the happy path, invalid data, target unavailable and duplicate submission, run in CI against a JDE test environment.
Testing and validation
- End to end: a new JDE item appears on the storefront with the right variants, entitlement and price for two test accounts in different price groups.
- The edge case that breaks a naive build: a price changes in JDE after the buyer loads the cart but before checkout. Prove that the buyer sees the change or the order is held, and never that it silently books at the old price.
- Run account matching against a copy of real JDE customers and measure how many match automatically, how many go to review, and how many would have been duplicated.
- Compare a sample of storefront prices against JDE's own price calculation for the same customer, item and quantity.
Phased rollout
Phase 1 — Connect and launch
- JDE current-state map, connectivity and integration users. Deploy the three System APIs, then scheduled catalog, pricing and account sync. Cart to rep with territory routing and the reconciliation report.
Phase 2 — Order-to-cash
- JDE order write, status, tracking and invoices back to the buyer. Tax and payment. Failure handling and duplicate protection.
Phase 3 — Scale
- Reuse the same System APIs for more storefronts, regions or ERPs. Add near real-time inventory, PO and net terms, quotes and punch-out as needed.
Success metrics
- Share of orders placed without customer service touching them.
- Cart-to-order conversion, and time from cart submission to first rep contact (Phase 1).
- Manual order entry effort per week, from the reconciliation report.
- Price mismatch rate between storefront and JDE invoice (target: none outside tolerance).
- Integration error rate, time to resolve an exception, and orders lost (target: none).
Troubleshooting
- Buyer sees a different price than the invoice — Likely cause: pricing sync ran before a JDE price change, or the buyer is in the wrong buyer group. Fix: check the price book version on the order, then the account's buyer group mapping.
- Duplicate accounts in Salesforce — Likely cause: accounts were created before the JDE address book number was stamped as the external ID. Fix: backfill the external ID, merge, then switch the sync to upsert-only.
- Leads reach the wrong rep — Likely cause: territory rules key on bill-to while the account keys on ship-to. Fix: agree the routing key with sales operations and map it explicitly.
- Orders stuck in "received" — Likely cause: JDE connectivity is down or the AIS integration user's password expired. Fix: check the exception queue and VPN status, and use a credential that doesn't expire.
- New items missing from the storefront — Likely cause: the JDE item has no price or entitlement. Fix: check the exception queue, where these are reported as validation errors.