Reconciling declared and received inventory across Salesforce and a WMS
A bidirectional integration that detects, quantifies, and displays the difference between what a merchant declared they would ship and what the warehouse physically received.
- Context
- A logistics and e-commerce fulfilment provider
- Role
- Salesforce Developer — owned the Salesforce side end to end
- Period
- 2024 — 2025
- Systems
- Salesforce ⇄ AWS Lambda / SQS ⇄ Warehouse Management System
At a glance
The business gained a systematic answer to a question it previously answered by hand, or not at all.
- Expected versus actual received is compared automatically on every receiving order.
- Discrepancies are visible per item, with direction and magnitude, rather than inferred later.
- Salesforce reflects what physically arrived, so downstream inventory decisions use accurate data.
Jump to
Overview
What this was
A merchant declares an incoming shipment; the warehouse physically receives it. Those two rarely match — short shipments, overages, and wrong SKUs are routine. This project made Salesforce the system of record for warehouse receiving, integrated it bidirectionally with the warehouse management system through an AWS pipeline, and surfaced the per-item discrepancy to the people who needed to act on it.
Business context
A logistics services company operating warehouse and fulfilment operations for merchants. Salesforce carried order management; the warehouse ran on a separate management system with its own scanning tool. Receiving accuracy directly affects inventory accuracy, and inventory accuracy affects everything downstream — what can be sold, what gets promised to a customer, and what a merchant is billed for.
Delivery timeline
- 01
Discovery
Traced the receiving process and where expected and actual diverge.
- 02
Data model
Designed the WRO header, expected items, and actual received items.
- 03
Integration surface
Built the inbound Apex REST endpoints and the outbound flow.
- 04
Pipeline
Designed the AWS capture/consume functions in both directions.
- 05
Reconciliation
Per-item discrepancy calculation triggered on receipt completion.
- 06
User experience
LWC presenting expected, actual, and variance side by side.
Business problem
Did the warehouse actually receive what the merchant said they would send? Nobody could answer that without opening two systems and comparing by hand.
No automated reconciliation
Expected versus actual received was never systematically compared.
Discrepancies stayed invisible
Over- and under-deliveries went unnoticed until they caused a downstream problem.
Inventory drifted from reality
Salesforce did not reflect what had physically arrived at the warehouse.
No view for users
No single place showed whether — and where — a receiving order was short or over.
Challenges
What made this harder than it looks
- 01
A direct callout would couple Salesforce to the warehouse system
If the warehouse system were unavailable, requests made straight from Apex would simply be lost, and the user or transaction would be blocked waiting on a system outside our control.
- 02
Volume exceeded what synchronous processing could carry
A single receiving order can carry a long list of items. Pushing each as its own synchronous callout runs into platform limits well before it runs into business volume.
- 03
Responses had to find their way back into Salesforce
The actual received items originate in the warehouse system. Getting them back into Salesforce reliably is a second integration problem, not a detail of the first.
- 04
The trigger point sat in the other system
Salesforce cannot know when physical receiving has finished. That signal belongs to the warehouse users, so the design had to treat an external status update as the start of a process rather than the end of one.
- 05
Discrepancy had to be per item, not per order
"This order is short" is not actionable. "This SKU is short by four units" is. The data model had to support item-level comparison rather than an order-level flag.
Architecture
How the system fits together
Salesforce is the system of record. AWS is the integration pipeline. A capture-then-consume model decouples the two systems in both directions, so neither one blocks or loses work when the other is unavailable.
- Shopify to Apex REST: orders & stock (asynchronous, queued, or scheduled)
- Apex REST to WRO + items: create
- WRO + items to Capture: DTO list
- Capture to Queue (asynchronous, queued, or scheduled)
- Queue to Consume (asynchronous, queued, or scheduled)
- Consume to Warehouse system: create order
- Warehouse system to Warehouse tool
- Warehouse tool to Apex REST: status = Completed
- WRO + items to Capture: request actual items
- Warehouse system to Consume: actual items (asynchronous, queued, or scheduled)
- Consume to Apex REST: post back (asynchronous, queued, or scheduled)
- WRO + items to Reconciliation LWC: reconcile
End-to-end flow
- 01
Create the receiving order
An external system calls a Salesforce Apex REST endpoint, creating the WRO record together with its list of expected items.
- 02
Push to the warehouse
Salesforce sends the order out through the AWS pipeline to be created in the warehouse management system.
- 03
Physical receiving
Warehouse users scan the items in the warehouse tool and mark receiving complete.
- 04
Status becomes Completed
The warehouse system updates the Salesforce record to Completed. This is the trigger point for everything that follows.
- 05
Fetch the actual items
That status change triggers an outbound request to retrieve the items the warehouse actually received.
- 06
Reconcile and display
Once the actual items are in Salesforce, reconciliation runs and an LWC shows expected against actual, with the discrepancy per item.
Technologies
Salesforce
- Apex REST (inbound endpoints)
- Apex callouts (outbound)
- Custom objects — WRO header, expected items, actual items
- Custom Metadata (endpoint configuration)
- Record-triggered automation
- Lightning Web Components
- DTOs and JSON parsing
AWS
- Lambda — capture function
- Lambda — consume function
- SQS queueing
External
- Warehouse management system API
- Warehouse scanning tool
- Shopify
Solution design
The choices that shaped everything else
Expected and actual are separate records, not two fields
Warehouse_Receiving_Order_Item__c holds what was declared. Actual_WRO_Item__c holds what arrived, with its own quantity and a signed discrepancy. Modelling them separately means an item present in one and absent from the other is representable — which is exactly the case a two-field design cannot express.
A signed discrepancy, not a boolean
The variance carries direction and magnitude. Short by four and over by four are different operational problems and are stored as different values, so both the UI and future reporting can distinguish them.
DTOs sit between the API contract and the schema
Inbound and outbound payloads are parsed into explicit data transfer objects rather than deserialised straight onto SObjects. The external contract can then change without forcing a schema change, and a malformed payload fails at a boundary that can report why.
The external status update is the trigger
Setting status to Completed from the warehouse is what initiates the retrieval of actual items. Salesforce does not poll and does not guess when receiving finished — it reacts to the system that knows.
The view answers the question directly
The LWC places expected, actual, and discrepancy side by side per item. The user is not asked to compare two lists themselves; the comparison is the interface.
Technical decisions
What was decided, why, and what it cost
Every decision below is paired with its trade-off. A design choice without a stated cost usually means the cost was not examined.
Split the pipeline into capture and consume functions
Rationale
Capture accepts the list of DTOs from Salesforce and returns immediately. Consume pushes them onward to the external system. The same pair works in reverse for responses coming back. Salesforce is therefore never blocked on the warehouse system, requests survive an outage instead of being lost, and queued DTOs absorb volume.
Trade-off
Two functions and a queue are more moving parts than one callout, and the flow becomes eventually consistent rather than immediate. Accepted because losing a receiving request is materially worse than showing it a few seconds late.
Hold the pipeline endpoints in Custom Metadata
Rationale
Endpoint URLs change between sandbox, staging, and production, and again whenever infrastructure moves. As metadata they are configuration; in Apex they would be a deployment.
Trade-off
One indirection between reading the code and knowing the target URL. Worth it to keep environment differences out of source control.
Make Salesforce the system of record for receiving
Rationale
Reconciliation, reporting, and the user-facing view all need one authoritative copy. Splitting that across two systems would recreate the original problem one layer up.
Trade-off
Salesforce carries the storage and the write volume for item-level data it does not originate.
Reconcile after receipt rather than incrementally during it
Rationale
A partially scanned order is legitimately incomplete. Comparing before the warehouse declares itself finished would generate discrepancies that are not discrepancies.
Trade-off
No live progress view during receiving. The business question is asked after receipt, so this matches how the information is actually used.
My responsibilities
What I personally owned
- Designed the data model — receiving order, expected items, actual received items, and their relationships.
- Built the Apex REST endpoints for inbound order creation and for receiving actual items.
- Configured the outbound callout to the AWS pipeline, with the endpoint held in Custom Metadata.
- Designed the capture/consume pipeline shape and the contract at each hop.
- Implemented the per-item reconciliation logic that computes the discrepancy.
- Built the automation that turns an external status change into a retrieval request.
- Built the Lightning Web Component presenting expected, actual, and discrepancy together.
Implementation process
How it was actually built
- 01
Started from the operational question
The requirement was not "integrate with the WMS" but "let users know if there is a discrepancy, and what it is". That framing determined the data model and the interface, and it is why the deliverable is a comparison view rather than a sync job.
- 02
Modelled the data before designing the integration
Getting expected and actual into separate objects with a signed variance made the reconciliation logic almost trivial. Most of the design effort went here, which is where it belonged.
- 03
Built the inbound surface first
With the Apex REST endpoints in place and testable against real payloads, the pipeline could be developed against a known contract instead of against an assumption.
- 04
Designed the pipeline for the outage case
The capture/consume split exists because the interesting scenario is the warehouse system being unavailable. Designing for that first is what made the happy path straightforward.
- 05
Closed the loop on the trigger
Wiring status = Completed to the retrieval request turned two independent integrations into one coherent process with a clear start and end.
Security considerations
What was protected, and how
Inbound endpoints are authenticated, not open
The Apex REST surface is reached by a named integration user with a permission set scoped to exactly the objects and fields the integration touches — not by an administrator profile.
No credentials or endpoints in source
Endpoint configuration lives in Custom Metadata and authentication through the platform credential store, so nothing sensitive is committed to version control.
Payloads are validated at the boundary
Explicit DTO parsing rejects malformed input before it reaches the data model, rather than partially writing records and failing midway.
The integration user is least-privilege
Object and field permissions are granted through a dedicated permission set, so the integration cannot read or modify data outside its scope.
Business value
The business gained a systematic answer to a question it previously answered by hand, or not at all.
- Expected versus actual received is compared automatically on every receiving order.
- Discrepancies are visible per item, with direction and magnitude, rather than inferred later.
- Salesforce reflects what physically arrived, so downstream inventory decisions use accurate data.
- Receiving work continues to be captured when the warehouse system is unavailable.
- Automated logistics and fulfilment workflows, reducing repetitive manual processing and improving operational reliability.
Lessons learned
What I took from it
The data model was the real design work
Separating expected from actual and storing a signed variance removed nearly all of the apparent complexity. What looked like an integration problem was mostly a modelling problem.
Decoupling is worth its extra parts
The capture/consume pipeline added infrastructure, but it converted an entire class of failure — the other system being down — from lost work into delayed work.
Configuration in metadata pays for itself immediately
Endpoints changed during delivery. Because they were metadata, each change was a configuration edit rather than a deployment cycle.
A per-record view answers today; reporting answers tomorrow
The LWC solved the immediate question well and revealed the next one: nobody could see discrepancy patterns across orders. Solving one question clearly is what makes the next one visible.
Future improvements
What I would do next
Listing the known gaps is part of the handover. A system described as finished usually means nobody looked hard enough.
A discrepancy dashboard across receiving orders
The LWC shows the discrepancy for a single order. Reporting and a dashboard across all receiving orders would let the business see trends by warehouse, merchant, product, or period — and act on recurring causes rather than individual cases. This is the single most valuable addition.
Improvement 1Automated alerting on threshold breach
A discrepancy above a configurable tolerance should notify a person rather than wait to be noticed, with the threshold held as metadata alongside the endpoint configuration.
Improvement 2Reconciliation history rather than current state
Retaining each reconciliation run would allow a receiving order to be audited over time, including re-counts and corrections.
Improvement 3
Keep reading