3PL Cutover

Mar 1, 2022

Nobody noticed the 3PL change

Two weeks, nine transaction types, and a cutover nobody noticed.

My manager called me, half-amused. "You won't believe this — one of the analysts just noticed we changed warehouse providers."

It had been two weeks since go-live.

Marketplaces were still selling, inventory was still syncing, and shipments were still going out, and for two weeks nobody on the operations team had noticed anything — until one of the analysts, reviewing inventory accuracy data, spotted something odd in a dashboard: the warehouse provider name had quietly changed from one to another.

Context

Razor Group is an e-commerce aggregator that acquires and operates digital-first consumer brands, selling across Amazon, Mercado Libre, and other marketplaces, all running through a NetSuite OneWorld environment that spans 12 countries and 300+ subsidiaries. Every marketplace order flows into NetSuite first and gets routed to the appropriate 3PL, while inventory syncs back hourly to keep marketplace listings accurate. If the integration stops, the entire chain breaks: orders pile up unrouted, customers don't get their shipments, and eventually marketplaces suspend the account for unfulfilled orders. In a setup like this, the integration is the operation.

In early 2022, Razor's Mexico-based 3PL informed us they wouldn't be renewing the contract because they needed the warehouse space for another client. The VP of Logistics selected a new provider, but there was no prior experience with them, no pre-built Celigo connectors, and no template to copy — so the integration had to be built from scratch in two weeks. The team raised the obvious concern, since that was a tight timeline for a from-scratch integration involving nine transaction types and inventory reconciliation across an active e-commerce operation, and they were right to be.

Approach

The stack was already decided — Razor used Celigo as the integration platform across all 3PL connections, so there was no architectural debate to have. The cutover strategy was also set by logistics: the team had been gradually redirecting inbound shipments to the new warehouse for weeks, leaving only a small inventory tail at the old location to fulfill orders during the transition, and by the time the new integration went live, the old warehouse was nearly empty. That part was good operations management, not engineering.

What was on me was the integration itself, which I'd never built in Celigo before. That sounds worse than it was — I'd worked with Razor's existing Celigo integrations enough to understand the platform's patterns, and from there it was mostly about applying engineering judgment to the specifics of this new 3PL's API. The real risk wasn't the platform — it was the timeline.

Two weeks for nine transaction types plus inventory reconciliation, with no template to copy, didn't leave room for guessing. Most of the open questions I hit had answers I could find in Razor's existing 3PL integrations — they used the same NetSuite back-end, so the patterns were already documented in the data, not in someone's head. Where the data was ambiguous, I'd build for both plausible interpretations rather than waiting on a meeting.

The other thing the timeline forced was an honest look at how integrations were typically built on this stack. The default approach would have been straightforward: one Celigo flow per NetSuite transaction type — one for sales orders, another for purchase orders, another for transfer orders, and so on for all nine. It works, but it scales badly, because every new transaction type means a new flow, and any payload change means touching multiple places. Building nine flows in two weeks was technically possible, but it was exactly the kind of easy-choice-that's-the-wrong-long-term-call that creates technical debt nobody is paid to clean up later.

So instead, I designed two patterns.

The first reorganized the outbound side around what actually mattered to the 3PL. Since the 3PL only cared whether a document was for shipping product out or receiving it in, I collapsed nine NetSuite transaction types into two Celigo flows grouped by that intent, and any future change had two surfaces to touch instead of nine.

The second pattern handled the inbound side back into NetSuite. Where the default would have been a dispatcher flow that detected the transaction type and delegated to a type-specific flow, I designed a self-describing payload that a single polymorphic flow could interpret directly — creating or updating the right record without intermediate routing. The dispatch logic was the payload itself, which meant fewer flows, fewer failure points, and one place to maintain instead of several.

Both patterns got reused in two more integrations after this one.

Solution

Three implementation concerns always show up in 3PL integrations and almost always get them wrong: idempotency, inventory event alignment, and reconciliation.

Idempotency lived in NetSuite's external ID field, constructed as a structured string with a source identifier, the 3PL's transaction type, and the event-specific reference. That handled duplicate inbound payloads cleanly, with Celigo's retry layer surfacing failed runs for engineering review on top. But the harder half of idempotency wasn't on my side — it was on the 3PL's. If the same business event ever got re-emitted with a different external ID, no construction scheme would catch it. That had to be agreed in discovery, where I confirmed the 3PL would always emit the same identifier for the same event regardless of internal retries.

The second concern is the one responsible for most 3PL accuracy problems, even though it rarely makes it into a project plan: when, exactly, the WMS increments and decrements its inventory, and whether that lines up with when NetSuite does. I've seen warehouses where inventory gets assigned manually instead of automatically, decremented only at ship time instead of on allocation, or received into a different bin or stock status than NetSuite assumes — each producing phantom discrepancies that look like integration bugs but are timing bugs. So during discovery I confirmed when the new WMS would decrement on outbound and increment on inbound, and although their default was manual allocation, they confirmed they could shift to automatic and eventually moved to a scheduled automatic process after go-live. That conversation, before any code was written, is what made the rest of inventory reconciliation tractable.

Reconciliation itself ran through a custom record called inventory variations that already existed in the environment. The 3PL's inventory snapshot landed there every six hours, and from there the data was exposed to the data warehouse and surfaced to operations through Tableau dashboards. No initial inventory load was necessary, because the cutover plan moved stock through physical transfer orders from the old warehouse to the new one — which meant the starting inventory was already represented in NetSuite as the destination of those transfers, and we avoided one of the most common failure modes in 3PL migrations: bulk initial loads that drift out of sync because the warehouse's first physical count doesn't match what the integration assumed.

One last detail worth mentioning: lines were consolidated by item before reaching the warehouse, since ten separate lines for the same SKU turn picking into ten times the work — a downstream cost the integration is in a position to prevent.

Results

The cutover went live on schedule, and the operations team continued running orders, shipments, and inventory through the system without flagging any change in behavior. Inventory accuracy, measured by comparing the WMS snapshot against NetSuite's available quantity, landed in the 96–99% range from day one and stayed there — which is worth contextualizing, because the Mexico operation had historically held that accuracy band even with the previous provider, while several of Razor's other warehouses across the world operated in the 60s. Internally the joke was that Mexico was the crown jewel of the fleet, and the cutover didn't change that. If anything, the fact that nothing changed is what made the project succeed.

Hypercare was scheduled for two weeks and consumed the standard rhythm of checking logs at the start of each day and again mid-shift, adjusting whatever showed up, and pushing fixes for any pattern that looked like it might recur. There were minor things along the way — there always are — but nothing that escalated, and no period of degraded operation beyond perhaps a couple of hours total across the entire window. The two design patterns that came out of the build, the outbound flow consolidated by 3PL intent and the polymorphic inbound flow driven by self-describing payloads, were later reused in two more integrations across the environment, which is the kind of secondary outcome that matters more than launch-day metrics: a pattern that holds up under reuse is one that solved the right problem.

What the project actually proved

The interesting thing about this case study isn't the speed, though two weeks for a from-scratch 3PL integration on an unfamiliar platform is faster than the industry norm. The interesting thing is that speed wasn't the goal — invisibility was. A successful 3PL cutover, in a live e-commerce operation where the integration is the operation, is one where nobody on the business side notices anything changed. That's a standard most cutovers fail to hit — there's usually a bad week of reconciliation errors, a quarter of inventory drift, a stretch of support tickets that everyone agrees to call "stabilization." None of that happened here, which is mostly explained by three decisions made before any code was written.

The first was refusing to default to one Celigo flow per NetSuite transaction type, because that pattern works in the short term and accumulates maintenance cost forever, while consolidating by 3PL intent and pushing dispatch logic into the payload itself produced an integration that was smaller, easier to change, and easier for the next engineer to read. The second was using discovery to align on event timing with the WMS before any data started flowing, since misalignment between when each system increments and decrements inventory is what causes most of the reconciliation problems people blame on integrations. And the third was skipping the initial inventory load by leveraging the physical transfer orders that the logistics team was already running, which avoided the most common single point of failure in 3PL migrations.

None of those three were technically difficult. They were judgment calls made at the right moment, which is what most senior ERP engineering actually comes down to.