Advanced Shipping and Fulfillment Logic Shopify Rules Can't Express
Shopify's native shipping settings express simple rules well: a price by weight or by order value inside zones you draw on a map. You need custom shipping logic when the correct rate depends on what is actually in the cart rather than a single number, when large or restricted goods do not fit parcel pricing, or when the real question is which warehouse should ship an order at all. The supported way to add that is the Carrier Service API, which lets your own code return live rates at checkout, paired with Shopify Functions, which let you hide, rename, or reorder the delivery options a shopper sees. Together they calculate the rate from real conditions and route fulfillment on purpose, without leaving the checkout your customers already trust. This is what to do once your shipping has stopped being simple.
The short version
- Shipping sits on conversion and margin at the same time. A rate that is too high loses the sale you paid to win; a rate that is too low turns a profitable order into a break-even one.
- The cart, not the total, decides the right rate. When specific SKUs, weight and dimension tiers, or product types change the number, zone-and-price rules cannot express it.
- Freight and hazmat are different worlds. Palletized freight, less-than-truckload quoting, and restricted-goods rules need logic, not another line in a rate table.
- Multi-origin is a routing decision, not a pricing one. With several warehouses the question is where to ship from and whether a cart should split, which native rules do not answer.
- The Carrier Service API is the seam for real-time rates. Your code receives the cart at checkout and returns rates it computed, so the number reflects what is genuinely in the basket.
- Shopify Functions curate what the shopper sees. Use them to hide an invalid option, rename a service, or reorder choices, so restricted or nonsensical rates never appear.
Why do shipping mistakes cost you at both ends?
Shipping is one of the few settings that touches revenue and profit in the same instant, which is what makes getting it wrong so expensive. Quote too high and the customer abandons at the final step, after you have already spent to acquire them, so the loss is the sale plus the acquisition cost that bought it. Quote too low and the order still completes, but you eat the difference on every unit, quietly converting sales that looked profitable into ones that barely break even. Fail to return any valid option at all, for an oversized item or a rural address, and the checkout simply stalls with no way forward, which reads to the shopper as a broken store rather than a shipping limit.
There is a further cost that hides behind the scenes, in the fulfillment decision the customer never sees. Shipping an order from the far warehouse when a nearer one had the stock, or splitting a shipment that should have traveled together, adds real cost to an order whose rate looked fine at checkout. So shipping logic protects the sale and the margin together, and the wrong logic bleeds you in places that never appear on the rate itself. That double exposure is why patching it with yet another app so often disappoints, and why it repays being built properly.
Where do native rules and rate apps run out?
Shopify's shipping settings and the common rate apps cover standard parcels at standard rates, which is the right tool for a store sending ordinary boxes. The limits show up as what you ship and how you ship it get more specific.
- Rates that depend on cart contents. When the correct number is driven by particular SKUs, weight bands, physical dimensions, or product types traveling together, simple zone pricing cannot represent it, and rate apps tend to offer a fixed menu of conditions rather than your actual formula.
- Freight and less-than-truckload. Large or palletized orders need freight handling and quoting, which behaves nothing like parcel pricing and which most setups cannot express at all.
- Restricted and hazmat goods. Products with carrier limits, hazmat handling, or destination restrictions need logic that decides what may ship where and by which service, not a flat entry in a table that has no idea the item is regulated.
- Choosing an origin, not just a price. With several stocking locations the hard part is deciding which one fulfills a given order, and how to treat a cart that should split across warehouses or deliberately stay whole, a decision native rules do not make.
- Delivery dates and local zones. Date selection, per-product lead times, and local delivery areas with their own rules are ordinary needs that off-the-shelf tools handle thinly if they handle them at all.
Read those as one underlying limit. Native settings let you describe simple shipping, but your business needs to compute it from real conditions and then route the fulfillment behind it, and that is a different capability than picking a row from a rate table.
What can custom shipping logic actually calculate on Shopify?
The reassuring part is that Shopify is built to allow exactly this without leaving the platform or touching the checkout customers rely on. The Carrier Service API registers your own service so that, at checkout, Shopify sends the cart to your endpoint and displays the rates your code returns, which means the number can reflect anything you can compute from the basket and the destination. Alongside it, the Delivery Customization Function API lets you rename, sort, and hide delivery options right in checkout, so shoppers only ever see the choices that make sense for their order. On that pair, a custom build can do the things a rate table cannot.
- Rate from the real cart. Compute the number from the exact SKUs, weight and dimensional tiers, product types, and destination, including dimensional weight where the box size drives the cost more than the scale does.
- Quote freight where parcel logic does not apply. Handle palletized and oversized orders with freight or LTL quoting, so the heavy end of your catalog gets a rate that matches how it really ships.
- Gate restricted goods. Apply hazmat and restriction rules so only valid services appear for regulated items, and an address that cannot legally receive something never sees an option that would fail.
- Route across origins and connect the warehouse. Decide where to ship from, treat carts that should split or consolidate on purpose, and hand the result cleanly to your 3PL or carriers, in the spirit of connecting your stack instead of copy and paste. Which location can actually fulfill depends on trustworthy stock, which is why this pairs with a real multi-channel inventory layer.
- Offer dates and local delivery. Present delivery-date selection, honor per-product lead times, and run local delivery zones with your real rules, curated by Functions so the wrong option is never on screen.
The discount and cart side of Functions works the same way and is covered in the Shopify Functions guide; if any of this reaches into the checkout surface itself, the checkout extensibility guide explains the supported boundaries. Deciding which of these to build first follows the same logic as what to automate first: start where the wrong rate is costing you the most, not where the code is most fun.
How do you build custom shipping without breaking checkout, step by step?
Because this logic runs inside the moment a customer is paying, the discipline is to prove it thoroughly before it goes anywhere near a live cart. This order keeps the riskiest surface in your store safe.
- Find the rate that is most often wrong. Look for the corridor or product type where you are either eating margin or losing sales, and make that the first phase rather than trying to model everything at once.
- Write the rate formula in plain language. State exactly what inputs drive the number, weight, dimensions, product type, origin, destination, before anyone writes a Carrier Service callback, so the logic is agreed rather than assumed.
- Stand up the Carrier Service endpoint against test carts. Return rates for representative baskets and confirm the numbers match your formula on ordinary orders before touching the awkward ones.
- Add the edge cases deliberately. Feed it the oversized order, the hazmat item, the remote address, and the multi-origin cart, and confirm it either returns the right rate or correctly returns none, so checkout never stalls silently.
- Curate the options with Functions. Hide, rename, and reorder services so the shopper sees a clean, valid set, and no restricted or nonsensical option ever surfaces.
- Wire the routing to fulfillment. Once the rate is right, connect the ship-from decision to your 3PL or carriers so an order lands at the correct warehouse without a person redirecting it.
- Run it beside the old setup, then cut over. Compare the new rates against your current ones on real orders until they hold up, and switch fully only when the numbers have earned it.
A shipping-logic readiness checklist
- Can you name the single rate that most often comes out too high, too low, or missing?
- Do you know every input that should drive that rate, and can you state the formula in a sentence?
- Is there a defined behavior when no valid rate exists, so checkout fails gracefully instead of stalling?
- Do restricted and hazmat items have rules that decide what can ship where, not just a price?
- For multiple warehouses, is there a clear rule for which location ships an order and when a cart may split?
- Are delivery dates and local zones expressed as real lead times rather than a rough guess?
- Is every rate and routing rule something you own and can change, rather than a fixed option inside an app?
Common pitfalls
The mistakes here almost all come from hiding complexity instead of expressing it, and each one leaks money at a spot you are not watching.
Flat-rating to paper over the hard cases. A single flat rate feels simple, but it silently overcharges the easy orders and undercharges the heavy or remote ones, so you lose some sales to a high number and margin to a low one at the same time. The flat rate is not a solution to complex shipping; it is a decision to be wrong in both directions.
Ignoring the no-rate case. Custom rate logic that only handles the carts it expects will occasionally return nothing for an unusual order, and a checkout with no shipping option does not explain itself to the shopper. The empty result has to be designed for, with a fallback or a clear message, or it becomes silent lost sales.
Testing only the tidy cart. A rate engine validated on a single normal order looks perfect and then fails the first time someone buys an oversized item to a rural address across two warehouses, which is precisely the order where the money is.
Consider a representative case, details changed. An equipment brand shipped everything on a couple of flat rates because their real costs were hard to model. Light accessories effectively subsidized heavy machines, so their best-selling small items were priced out against competitors while every large order to a distant region lost money on freight the flat rate never covered. They also listed a few palletized products as if they shipped by parcel, so those orders were mispriced by a wide margin and generated support calls when the carrier could not actually take them. Moving to a Carrier Service that computed rates from weight, dimensions, and destination, quoted freight for the palletized lines, and routed each order to the nearer of two warehouses did three things at once: the small items became competitively priced, the heavy orders stopped bleeding margin, and the impossible parcel quotes disappeared because Functions hid options the goods could not use. Nothing exotic was involved. They had simply been asking a rate table to do a rate engine's job.
FAQ
Can Shopify calculate shipping based on what's in the cart?
Native rates key off weight or order value within zones, which cannot express a number that depends on the specific mix of items. To rate from cart contents you use the Carrier Service API, which sends the cart to your own endpoint at checkout and shows the rates your code returns. That lets the price reflect particular SKUs, weight and dimensional tiers, product types, and destination together, so a basket of light accessories and a basket containing a heavy machine are quoted differently even when their order totals happen to match.
What is the Carrier Service API?
It is Shopify's supported mechanism for real-time, externally calculated shipping rates. You register a carrier service with a callback URL, and at checkout Shopify posts the cart and destination to that URL and displays whatever rates you return. Because the calculation happens in your own code, the rate can be based on anything you can compute, which is what makes freight quoting, dimensional pricing, and cart-aware rules possible. It is the seam through which custom shipping logic reaches the checkout without you having to modify the checkout itself.
How do I ship freight or LTL orders on Shopify?
Freight does not fit parcel pricing, so you handle it by computing freight or less-than-truckload rates in your own Carrier Service logic and returning them at checkout for the orders that qualify, typically the large or palletized ones. You can also use Functions to make sure parcel options never appear for goods that physically cannot ship that way. The result is that heavy orders receive a rate that matches how they actually move, instead of a parcel estimate that either loses you money or fails when the carrier declines the shipment.
Can I ship from multiple warehouses and pick the closest?
Yes, but that is a routing decision layered on top of rating, not something native rules resolve. Custom logic can look at where stock is held and the destination, decide which location should fulfill an order, and determine whether a mixed cart should split across warehouses or stay together. That decision then flows to your 3PL or carriers so the order is fulfilled from the right place. Because the choice depends on knowing what is genuinely in stock at each location, it works only on top of an inventory source you actually trust.
How do I stop invalid shipping options from showing at checkout?
Use the Delivery Customization Function API, which lets you hide, rename, and reorder delivery options in checkout based on the order. If an address cannot legally receive a hazmat item, or a service does not apply to what is in the cart, the Function removes that option before the shopper ever sees it. This keeps checkout clean and prevents the frustrating case where a customer selects a rate that later turns out to be impossible, which is both a support cost and a lost order.
Will custom shipping rates slow down or break my checkout?
Not when built correctly, because the Carrier Service pattern is designed for exactly this and the main risk is a slow or empty response rather than the mechanism itself. A well-built service responds quickly, caches where it can, and always returns a sensible fallback rather than nothing, so checkout never stalls waiting on your logic. The way to keep it safe is to test the awkward carts and the failure cases deliberately before going live. If your shipping has outgrown what settings and apps can express, tell me where the rate keeps letting you down and I will sketch a first phase to get both the rate and the routing right.
Have a project in mind?
Let's turn it into custom software that moves your business forward.