Turning messy carrier data into invoices that hold up
Warehouses that ship other companies' packages lose real money in the gap between what carriers charge them and what they bill their customers. ShipTide was built to close that gap.

Where this stands
As of August 2026, ShipTide has a working quote-to-price pipeline, carrier rate card ingestion, and multi-tenant auth running against a real database, and active development is paused while other work takes priority.
Picture the billing lead at a 3PL doing month-end close. Carrier invoices arrive weekly as enormous files, one line per package, full of surcharges nobody quoted. The warehouse system has its own record of what actually shipped. The customer contract lives in a spreadsheet somebody built two years ago and nobody has opened since. Getting from those three sources to one invoice the customer will pay without arguing is a week of manual work, every month.
Discovery
The same spreadsheet problem shows up earlier, at the quoting stage. When a 3PL wants to win a new account, the prospect sends a file of their historical shipments. Someone opens it, works out what the columns mean, looks up the origin and destination ZIP pair on each row to find the carrier’s zone, hunts down the right cell in a rate card, applies markup, adds fuel, and totals it. The prospect files are never in the same format twice. The rate cards are not much better: UPS publishes one service per sheet, FedEx stacks several services vertically inside a single sheet, and DHL only publishes PDFs.
Two things constrained the design. The data never arrives clean, and any product that assumes otherwise dies during onboarding. And a 3PL will not rip out its warehouse management system to try billing software. The WMS is where receiving, picking, inventory, and labor tracking already live. Asking an operator to replace it is asking them to bet the business on a tool they have not used yet.
Before any code existed, we wrote up a teardown of an earlier platform in this same category, plus a competitive read on where the market was already crowded. That reading was useful mostly as a list of things not to repeat: a 5,000-line data mapper with shipping logic tangled into its core, a hand-rolled rules engine spread across roughly 250KB of Python, and a custom sync orchestrator built around one WMS vendor’s API. Each of those was a maintenance tax that bought nothing a customer could see.
Key Takeaway
Not replacing the WMS is the whole strategy. ShipTide sits on top of whatever a 3PL already runs and takes responsibility for one thing: the money. That narrows the product enough to actually finish it, and it drops the adoption question from 'migrate your operation' down to 'send us a file.'
What We Built
The first thing built was not ShipTide. It was Anvil, a separate TypeScript data mapping engine that takes an arbitrary spreadsheet, infers its shape, and proposes a mapping into a target schema. An LLM handles the semantic guesswork about what a column named 'Ship To Zip' or 'dest_postal' is supposed to mean. Anvil knows nothing about shipping. ShipTide plugs into it through a domain package that supplies the carrier rate and shipment schemas along with the load adapters, so Anvil can be pointed at an entirely different domain by a different product later.
On top of that sits the bid workflow, a four-phase wizard: Collect, Process, Price, Deliver. Collect takes the prospect's shipment file and walks the user through the generated column mapping, shown either as a visual set of connection lines or as a details table. Process runs the ETL, resolves each shipment's origin and destination ZIP pair to a carrier zone, then matches it against imported rate cards, rounded up to the next carrier weight break the way the carriers do it themselves. Price opens the markup editor. Deliver produces the review screen and the export, including a net rate sheet and a shipment-by-shipment comparison against what the prospect pays today.
Carrier rate cards get their own import pipeline. An LLM inspects a sample of sheets from an uploaded XLSX and returns a config describing where the rate data starts, which column holds weight, which columns are zones, and what service each sheet represents. A deterministic extractor then does the actual parsing from that config, so the model is used to recognize layout rather than to read numbers. Rate cards are versioned with full history and per-version ingest details, because a rate card that changed quietly is a billing dispute waiting to happen.
Markup resolves in a defined order. A rule set on a specific service beats one set on a carrier, which beats the account default. Rules are either a percentage or a flat amount, with an optional per-shipment floor. Fuel surcharges apply separately at different rates for ground and air, since the carriers treat them differently. Every quoted rate stores its base cost, its markup amount, the rule that produced it, and its fuel component, so any number on an invoice can be taken back apart later.
The stack is Next.js 16 and Postgres with Drizzle, tRPC between the web app and a NestJS server, Temporal for long-running jobs, and Better Auth for organization-scoped tenancy. Where the old platform hand-built its infrastructure, ShipTide's architecture decisions mostly say reuse: GoRules for the business rules engine instead of custom condition trees, dlt as a small Python sidecar for data sync instead of a bespoke orchestrator, one partitioned Postgres instead of a two-database split. Those calls are written down as ADRs in the repo with the reasoning attached, which is why the project can be picked back up without re-deriving any of it.
Picking It Back Up
Work stopped at the start of April 2026 with quoting functional and billing not yet begun. Resuming means picking up at the exact point the strategy memo always identified as the step that gets paid for.
- Billing: reconciling carrier invoices and WMS records against contract rules to produce customer invoices. The dashboard routes exist as placeholders; the logic behind them does not.
- Bill now, true up later: carrier invoices arrive late and incomplete, so the system has to bill on estimates and correct in a later period without breaking the audit trail.
- Organization plugin and role-based access: login, verification, and password reset already work, but invitations and per-role permissions are still unchecked boxes.
- Zone normalization: UPS labels a zone '102' and FedEx labels the same thing '2'. Mapping carrier-specific labels onto standard zones is a known follow-up.
- Tenant scoping in the mapping cache, which is on the hardening list before the system goes anywhere near real customer data.
The work is real, it runs, and it is documented well enough that restarting is a matter of days rather than weeks. Whether it restarts depends on whether a 3PL with this problem shows up wanting it finished.
Is your margin buried in a spreadsheet?
If your business runs on files nobody trusts and math nobody wants to redo, that is a solvable problem.
Schedule a Conversation