Connect Fulfillment Without Copy-Paste: Shopify Integrations Done Right
Sending every order to your fulfiller by hand costs you hours per week. And the first transposed digit in an address costs you a customer.
I've built this integration for nano., mate and MUSTAX, plus a number of Flowhouse clients. The setup looks similar every time. So do the mistakes. So here's the sequence I always use now, including the places where it breaks in practice.
What actually flows between shop and warehouse
Before you touch any tool, write down which data goes in which direction. There are exactly two directions and a handful of events.
From shop to warehouse:
- The order: order number, line items with SKU (the unique article number) and quantity, delivery address, requested shipping method.
- Changes to the order, as long as it hasn't been packed.
- The cancellation request.
From warehouse back to shop:
- The shipping confirmation with carrier, tracking number and the line items that actually shipped.
- Current stock per SKU.
- The receipt of a return.
That's it. Everything else is optional. If you don't have this list straight, you'll build an integration that works in the normal case and goes quiet in the special case.
One point almost always slips through: stock. If you sell on several channels, the message coming back from the warehouse is your single source of truth. How we set that up is in Multichannel Inventory Management.
File transfer or API: the trade-off
Many fulfillers offer both. CSV files over SFTP (a file server with encryption) or an API (a programming interface that lets systems talk to each other directly).
| Criterion | File transfer | API |
|---|---|---|
| Setup | fast, often in a day | 3 to 8 days, depending on the docs |
| Delay | 1 to 24 hours depending on the schedule | seconds |
| Errors visible | only on the next run | immediately in the response |
| Changes to the order | practically impossible | possible |
| Ongoing maintenance | field order breaks quietly | version changes are announced |
My rule of thumb: under 20 orders a day, with a fulfiller who only speaks CSV, file transfer is perfectly fine. As soon as customers ask about tracking before your file has even run, the API becomes the better choice.
The underrated downside of files is the silence, more than the delay. A broken line in a CSV gets skipped at the fulfiller without a word, and you find out three days later through a customer email. An API answers you immediately with an error code.
The events you react to
The clean way is event driven, so leave the scheduled polling alone. Shopify sends you a message the moment something happens. That's called a webhook. If the term is new to you: Webhooks Explained covers it in ten minutes.
Three webhooks are enough to start:
- `orders/paid`: order is paid, cleared for the warehouse.
- `orders/cancelled`: cancellation attempt, to the warehouse as fast as possible.
- `orders/updated`: address or line items have changed.
On the other side you need an address the fulfiller can send its shipping notification to. If it can't do that, you poll its interface every 15 minutes for new shipments. It's clumsy, and it works.
Between the two sides we run n8n. It receives the webhook, translates the fields and calls the other side. If you've never worked with it: Your First n8n Workflow is the shortest way in. What our complete order system looks like is in Order Processing Without Staff.
The fields where it falls apart
Translating between shop and warehouse is the part everyone underestimates. Four classics from our own integration:
SKUs don't match. In the shop the item is `NANO-BLK-2ER`, in the warehouse it's `1004237`. You need a mapping table, and it belongs in exactly one place. Not spread across three workflows.
The warehouse doesn't know bundles. Your two-pack is one item in the shop and two in the warehouse. Solve that during translation, not in the stock levels.
Address fields don't line up. Shopify has `address1` and `address2`, many warehouse systems separate street and house number. Splitting the street automatically fails on roughly every twentieth German address. Ask for the house number separately at checkout, or let the fulfiller split it.
Shipping methods are named differently everywhere. `Standard` on your side, `DHL_PAKET_NATIONAL` at the warehouse. That belongs in the mapping table too.
Edge cases that catch up with you later
The normal case is built in a day. The remaining days go into these four cases, and they decide whether your support stays calm.
Partial delivery. The warehouse only has two of three line items and ships anyway. If your integration then marks the order as fully shipped, the customer gets a tracking number for a parcel that's missing their favorite item. Always take the line items that actually shipped from the response, never the original order.
Cancellation after handover. At a good fulfiller, "customer cancels" and "parcel is packed" are sometimes 20 minutes apart. Your cancellation call has to handle three answers: cancelled, too late, unknown. On "too late" you have to treat the case as a return and must not simply refund the money.
Duplicate handover. Webhooks sometimes arrive twice, which is normal and not a bug. So always send the order number as a unique key and let the fulfiller reject what it already knows. Without that you'll eventually ship a parcel twice.
Address change mid-flight. The customer writes two hours after ordering. In our setup, the automation isn't allowed to decide that on its own. The workflow checks whether the parcel has already been packed, then puts the case in front of a human.
Together these four cases are maybe three percent of orders. They account for most of the support emails that come in afterwards.
How to test before real goods go out
Testing doesn't mean pushing one order through and calling it a day. This is the order I always use:
1. Ask for a test environment. Almost every fulfiller has one. If not, agree on a test SKU that never ships.
2. One order with a single line item. Check field by field against what arrives in the warehouse system. Not just "it arrived".
3. One order with a bundle, special characters and a long address. Umlauts, apostrophes and a 60-character company name surface most formatting errors.
4. The four edge cases above, one at a time. Trigger each one deliberately and watch what your system does.
5. Check the return path. Tracking number lands in Shopify, customer gets exactly one email, not three.
6. First week in shadow mode. The automation runs, and a human spends ten minutes each evening looking it over.
If the API terms in your fulfiller's docs are unfamiliar, API Basics for E-Commerce helps you read them.
Data protection: addresses are personal data
The moment you hand names and addresses to your fulfiller, you're processing personal data on someone's behalf. Three things need to be settled before the first real order flows.
Data processing agreement. Every fulfiller needs one, and so does every system in between. If your n8n runs at a hosting provider, that provider belongs in your record of processing activities too. This is the GDPR paperwork every EU shop has to keep.
Server location. We run n8n self-hosted in Germany. That isn't required, but it keeps the answer to customers short.
Minimization and retention. The fulfiller needs name, address, line items. It doesn't need payment data, order history or marketing consent. And your workflow logs shouldn't keep real names around for months. We delete execution data after 30 days.
The limits
So you don't talk yourself into anything:
- An integration doesn't fix a bad fulfiller. If they pack the wrong things, you just see it faster afterwards.
- Special cases stay manual. A parcel stuck in customs, a wrong batch, a B2B order shipped on pallets: no workflow pays off there.
- Setup takes longer than the normal case suggests. Budget 3 to 8 days for a proper API integration, not one afternoon.
- You need monitoring. A workflow that fails silently is worse than doing it by hand. In our setup, Slack reports every failed run immediately.
Conclusion
The integration itself isn't hard. The hard part is the care that comes before it: which data flows, what the fields are called on both sides, what happens on partial delivery and cancellation.
Work through that list before you build, and the integration will run for years without you. Skip it, and you've replaced copy-paste with silent errors, which is a bad trade.
If you have your fulfiller's docs in front of you and aren't sure whether API or file is the right choice: drop us a line at Flowhouse. We'll look at it with you, without a pitch.