Supply Chain Visibility Beyond Dashboards
Most supply chain visibility tools paint a dashboard over broken data. Real visibility lives in the WMS-TMS-carrier integration layer underneath.
It’s 3:14pm on a Tuesday. A Fortune 500 retailer’s distribution VP is on a call with their largest 3PL because a 47-pallet inbound from a CPG vendor went silent two days ago. The TMS still says In Transit. The WMS expected it Monday and has rolled the dock door to Wednesday. The carrier’s portal — accessed through a saved bookmark — shows the trailer was delivered Sunday at 2:11am to a different facility. None of the four systems agree, and the only person in the building who could reconcile them is on PTO.
This is what most enterprises mean when they say they “have a supply chain visibility problem.” It is not a dashboard problem. The dashboard probably exists. It’s a data problem — four operational systems, four definitions of the same shipment, and no canonical layer underneath that can answer the question where is this load right now and what should we do about it.
Vendors call the answer a control tower. Industry analysts call it an operating picture. The work itself is unglamorous: stitching WMS, TMS, carrier APIs, and order management into one event stream, on one identity model, with a latency budget that matches how fast operators actually need to act. It is the prerequisite to every interesting capability layered on top — exception management, predictive ETA, AI agents triaging stuck shipments. Without it, you get a dashboard. With it, you get supply chain visibility that actually changes operating decisions.
The post below is the data architecture that gets you there, drawn from the digital transformation engagements we’ve run for distribution operators with five-figure daily shipment volumes across multiple 3PLs.
Why “control tower” projects keep underdelivering
The pattern is consistent. A logistics ops leader buys a control tower platform — Project44, FourKites, Shippeo, an in-house equivalent — that promises a unified shipment feed. Eight months in, the platform is live, the dashboard is pretty, and the operations floor is still using their saved carrier bookmarks because the platform’s ETAs are wrong, the exception alerts fire on closed shipments, and inbound dock plans are built off the WMS, not the tower.
Three structural reasons keep showing up:
- The platform federates the symptoms, not the data. It stands up adapters to each carrier and each TMS, normalizes status codes into a generic taxonomy (
In Transit,Out for Delivery,Exception), and shows you the result. The translation throws away the carrier-specific signal that operators learned to trust over years. Now the dashboard says “Exception” but a planner can’t tell whether that’s a hard refusal or a five-minute appointment slip. - The identity layer is implicit and wrong. Tower-platform identity is usually keyed on the carrier’s tracking number or PRO. That works until a shipment splits across two trailers, or gets re-tendered to a different carrier mid-route, or transits a yard with a different inbound ID. Each of those is common. The identity needs to live at the shipment, not the tracking event, with stable resolution back to the order, the SKUs, and the customer commitment.
- The latency budget is not enforced. “Real time” in tower marketing usually means the ingestion pipeline is real time. The decision it informs may be on a 4-hour cron, because the alerting layer batches events for analytical convenience. Operators acting on a 4-hour-old exception have already missed the window where the action mattered.
The fix isn’t a different platform. It’s a data foundation underneath whichever platform you use — a foundation you own, that resolves identity correctly, preserves the source-of-truth signal, and respects the actual decision latency. Most of this post is what that foundation looks like.
The four data sources and what each actually owns
A working operating picture for distribution logistics joins four signal classes onto one shipment timeline:
| Source | What it owns (and what it doesn’t) | Typical cadence |
|---|---|---|
| Order management (OMS / ERP) | The customer commitment: SKU, quantity, promise date, ship-to. Not where the goods physically are. | Transactional; sub-second on writes |
| WMS (Manhattan, Blue Yonder, Körber, in-house) | Inventory state inside the four walls of a DC: receiving, putaway, picking, staging, dock. Not anything off-prem. | Sub-minute event stream; daily snapshots |
| TMS (Oracle OTM, MercuryGate, e2open, Manhattan TMS) | Planned freight: tender, route, lane, carrier, planned ETA, rate. Not real-world status. | Bursty; tender events + nightly settlements |
| Carrier APIs / EDI (12+ carriers in a typical multi-3PL stack) | Real-world tracking events: pickup, in-transit milestones, delivery, exception. Not the order or the SKU. | Per-carrier; ranges from 5-minute polling to push webhooks |
Two implications come out of that table that determine the whole architecture.
First, no single system has the question’s answer. “Where is this customer order right now and is it going to make its promise date?” requires the OMS for the promise, the TMS for the planned route, and the carrier feed for the actual location — at minimum. The WMS joins when the shipment is inside a DC. The honest answer to the original 3:14pm question requires reconciling all four.
Second, the source-of-truth assignment per attribute is not negotiable. Promise date is OMS. Planned ETA is TMS. Actual location is carrier feed. WMS owns inventory state, not transit state. When two systems disagree — and they will — there has to be a written rule about which one wins, per attribute, and that rule has to survive a re-org. Without it, every escalation becomes a debate about which screen to trust.
The stitching problem: identity resolution across the four
This is where most projects quietly fail. The four systems use different identifiers for what an operator thinks of as “the same shipment”:
- OMS identifies by sales order number + line + ship-to.
- WMS identifies by warehouse shipment ID + waveID + license plate.
- TMS identifies by shipment ID + tender ID + load ID.
- Carrier feed identifies by tracking number, PRO, BOL, container number, or trailer number — sometimes more than one for the same load.
A working operating picture needs a canonical shipment identity that resolves all four down to one stable identifier, with full bidirectional traceability. The mistake we see most often is teams trying to use the carrier’s tracking number as the canonical key. It looks convenient and breaks the first time a shipment is re-tendered, splits across two trailers, or transits a cross-dock with a different inbound ID. Tracking numbers are evidence, not identity.
The pattern that holds up:
- Issue an internal
shipment_uuidat the point the OMS commits the order to a TMS tender. - Maintain a resolution table that maps
shipment_uuidto every external identifier that ever attaches to it — TMS load ID, WMS shipment ID, carrier tracking numbers, container numbers, BOL numbers — with the time window each was active. - Treat the resolution table as append-only. When a shipment splits, you create child UUIDs and link them; you don’t overwrite history.
- Every downstream consumer (control tower, analytics, AI agent) joins on
shipment_uuid, not on the source-system key.
This is the same identity-resolution discipline that shows up in master data management — and the data integration work it requires is essentially MDM applied to the shipment domain. It’s not optional. The operating picture is only as reliable as the identity layer it sits on.
Three secondary identity problems trip up almost every multi-3PL operator, and each deserves its own resolution table:
- Location identity. Your DC is
DC-04. Your TMS calls itWHSE_004_ATL. Your largest carrier calls itAtlanta-NE-Cust12345. Their EDI 214 sometimes geocodes to a lat/long that’s the truck stop two miles north. None of these auto-join. A canonical location dimension with mappings to every system’s local identifier is load-bearing. - SKU identity across banners and trading partners. A multi-banner retailer’s SKU in OMS is not the same as the vendor’s SKU on the inbound, which is not the same as the GTIN on the case label. For a CPG operator, distributor SKUs and retailer SKUs add another two layers of mapping.
- Carrier identity. Saia, Saia LTL Freight, and Saia Inc. are the same carrier. Your spend analytics will not believe this until someone fixes it.
Carrier APIs are the hard part — and they’re not your data
The harder half of the visibility problem is that the most operationally important feed — the actual tracking events — is the one you control least.
A typical multi-3PL distribution operator pulls tracking from anywhere from 12 to 80 carriers across LTL, TL, parcel, intermodal, and ocean. Each carrier exposes some combination of:
- EDI 214 (transportation carrier shipment status) — the lowest common denominator, batch, status-code-driven, and roughly four decades old.
- REST APIs with carrier-specific schemas, response shapes, and rate limits — Project44 and FourKites both exist largely because normalizing this surface across 80 carriers is genuinely hard work.
- Push webhooks — preferable for latency, available from a minority of carriers, with carrier-specific retry semantics and out-of-order event delivery.
- Portal scraping — for the long tail of regional carriers with no API at all. Brittle, slow, and the operational reality.
Three architectural commitments make this layer manageable:
- A canonical event model. Define your own
ShipmentEventschema once —shipment_uuid,event_type,event_timestamp,location,source_system,confidence,raw_payload— and translate every carrier’s representation into it on ingress. The raw payload stays for forensic audit; the canonical event is what downstream systems read. The translation layer is where carrier-specific signal is preserved with attribution, not flattened. - Separate transport from semantics. Whether a status arrived via webhook, polling, or batch EDI is a transport detail. The downstream system shouldn’t know. A unified event bus (Kafka, Kinesis, Event Hubs — pick the one that matches your cloud) sits between the carrier adapters and every consumer.
- Out-of-order events are normal. Carriers backfill, retry, and revise. A delivered event sometimes arrives before its in-transit predecessor. The event store has to be temporal — you query “what did we know about this shipment at 3:14pm” and get a deterministic answer regardless of when the events arrived. Reasoning about exceptions without temporal queries quietly gives you false alarms and missed escalations in roughly equal measure.
Latency budgets — and why “real time” means three different things
Operations doesn’t actually need every signal in milliseconds. It needs the right signal at the right tier of latency, and it’s ruinous to under-spec or over-spec either one.
A workable latency model for distribution logistics has three tiers:
| Tier | Latency budget | Use case | What sits here |
|---|---|---|---|
| Decision | < 5 minutes | Exception triage, dock door reassignment, customer ETA updates | Carrier webhooks → event bus → operator queue |
| Operational | < 1 hour | Wave planning, carrier capacity rebalancing, cross-dock decisions | Hourly batch from event store + WMS |
| Analytical | < 24 hours | OTIF reporting, lane-level cost analytics, carrier scorecards | Daily ETL into the data lake / warehouse |
Conflating these tiers is what produces the 4-hour exception alerts on the control tower’s pretty dashboard. Exceptions belong in the decision tier or they’re not exceptions; they’re history. Lane-level analytics in the decision tier is wasted infrastructure cost — the answer doesn’t change in a five-minute window.
The architectural consequence is that your event store needs two read paths: a low-latency operator path (the bus and a hot store) for decision-tier consumers, and a batch path (the lake) for the operational and analytical tiers. Trying to serve both from one storage layer is a common mistake — Snowflake-as-control-tower-backend and Kafka-as-analytical-store both fail in their respective ways.
The S.C.A.L.E. pattern for multi-3PL logistics ops
The reference shape we deploy on multi-3PL logistics engagements has five layers, mapped to the S.C.A.L.E. data foundation we anchor heavy-vertical projects on:
- Connect. Carrier adapters (REST, EDI 214, webhooks, portal scrapers for the long tail), TMS connectors, WMS event tap, OMS CDC. Each adapter normalizes into the canonical
ShipmentEventandOrderEventschemas on ingress. - Centralize. A unified event bus (Kafka or cloud-equivalent) plus a temporal event store (Iceberg or Delta on object storage). The lake is the system of record; the bus is the live transport.
- Conform. The shipment, location, SKU, and carrier identity-resolution services. This is the layer that makes joining the four sources possible. Most of the engineering effort goes here, and most of the business value compounds from getting it right.
- Consume. Two read paths — operator-facing low-latency (often a Postgres or DynamoDB hot store fed by the bus) and analytical (data warehouse or lakehouse fed by the lake).
- Govern. Access boundaries (carriers see only their own data; 3PLs see only their lanes; customer portals see only their shipments), audit logging on every read, and the master-data services that own canonical identity. Compliance posture matches the regulated mode that applies to your operation — customs and HOS data carry different controls than commercial freight metadata.
Cloud choice tends to follow the operational center of gravity. AWS-leaning shops land on Kinesis + S3 + Iceberg + Postgres for the hot store. Azure-leaning shops use Event Hubs + ADLS Gen2 + Delta + the Fabric stack for analytics. The pattern is the same; the parts catalog is different. The point of the foundation is that the layer above — the control tower, the customer portal, the AI agents — runs on the canonical event stream and the canonical identity, not on whichever carrier API happens to be most convenient that week.
Where AI agents fit (and where they don’t)
The temptation, once the operating picture is real, is to immediately point an AI agent at the exception queue and let it triage. That’s the right end state — and we’ll go deep on the agent layer in an upcoming post on AI agents for distribution-center exception management. But the order matters.
Agents that triage shipment exceptions need three things that only the foundation provides: a stable shipment identity (so the agent’s actions don’t double-fire on the same load under two different IDs), a temporal event store (so the agent can reason about what was known when), and reliable identity resolution to the customer commitment (so the agent’s escalation gets routed to the right account team). Without those, the agent is at best a slightly faster version of the dashboard nobody trusts.
With them, the same agent class we’ve been deploying on the generative AI side of the business can absorb the routine exception triage work that currently consumes a third of an ops team’s day. The pattern fits naturally on top of S.C.A.L.E. — but only on top of it.
Where to start — a 30-day visibility audit
If you’ve got a control tower that the operations floor doesn’t trust, or you’re scoping a multi-3PL visibility build, the highest-leverage diagnostic is not a vendor RFP. It’s a four-week audit of the four data sources and the joins between them.
- Week 1 — Source inventory. Catalog every OMS, WMS, TMS instance and every carrier feed currently flowing in. Count the hidden ones — the spreadsheets a planner maintains, the email threads that constitute “tribal” exception handling.
- Week 2 — Identity audit. For 50 sample shipments selected across lanes and carriers, manually trace the identity chain. How many systems can a planner walk through to confirm the same load? How often does the chain break?
- Week 3 — Latency audit. Per source, measure end-to-end latency from real-world event to operator-visible signal. Compare against the decision-tier budget the business actually needs.
- Week 4 — Exception archaeology. Pull the last 90 days of escalations. For each, trace which signal could have caught it earlier, in which source, with what latency. The pattern that emerges from this is your priority list.
The output of those four weeks is a sequenced foundation plan with the specific connectors, the identity model, and the latency tiers that match your operation. From there the build is sequenced — connect, centralize, conform — and the operating picture goes live before the legacy bookmarks get turned off.
The 3:14pm Tuesday call doesn’t go away because you bought a control tower. It goes away because the four systems agree on what shipment they’re talking about, the carrier event stream is honest about when it landed, and the exception fires inside the window when an operator can still do something about it. That is what supply chain visibility actually looks like — and it lives in the data layer, not the dashboard layer.
Founder & CEO, Algoscale
Neeraj has led AI and data engagements for Fortune 500 clients across finance, healthcare, and retail. He writes about what actually ships — not what looks good in a slide.