Notes
What not to sync between a store and an ERP
Sync what the business transacts on: orders placed after go-live, stock, fulfillments, and the customer and item keys that join them. Closed historical orders, values one system calculates, payment data and content the store owns stay where they are, because every synced field is a rule that has to keep working for as long as the integration runs.
Updated
The short answer
Sync the data both systems transact on and nothing else: orders from go-live onward, stock, fulfillments and tracking, and the customer and item keys that let the two systems find each other's records. Everything else has an owner, and it stays with its owner.
| Data | Default | Why |
|---|---|---|
| Orders placed after go-live | Store to ERP | Revenue, picking, invoicing |
| Stock per location | ERP or WMS to store | What can be sold |
| Fulfillments and tracking | ERP or WMS to store | The customer sees the shipment |
| Customer and item keys | Both ways, once per record | Joins orders to accounts and items |
| Orders closed before go-live | Stay in the source | Already posted; still readable there |
| Tax totals, available quantity, average cost | One way, from the system that calculates them | Two calculations disagree |
| Card numbers, passwords | Never | Neither system needs them |
| Descriptions, SEO fields, collections | Owned by the store after the first load | Written and checked there |
The list is short on purpose. Each row is a set of mapping rules, and each rule is checked on every record of every run.
Every synced field is a rule that runs forever
A field in the integration is more than a column. It is a mapping rule, a validation, a test case, and one more thing to check when either side changes its API. Shopify releases a new API version every quarter and supports each one for at least 12 months. In version 2026-10 the variant field barcode is marked deprecated in favor of barcodes, a list of up to 20 typed barcodes. An integration that writes barcode has to be changed before that field goes away; one that never mapped barcodes has nothing to change.
The same count applies on the ERP side. NetSuite's item record has hundreds of fields, and most of them are planning, costing and accounting fields: autoreorderpoint, costingmethod, cogsaccount. A store needs a handful. Mapping 12 fields means 12 rules to keep working; mapping 60 because they were available means 60, and the 48 extra are the ones nobody looks at until one of them breaks a run.
A field that turns out to be needed later is added as one rule plus a single backfill run over the existing records. That is cheaper than carrying an unused field for years.
Which items to send: not the whole item master
An ERP holds every item the company ever bought, sold, phased out or set up for a single quote. The store needs the ones it sells. NetSuite marks those with the checkbox isonline (Display in Web Store); isinactive hides an item from lists. Exact Online has IsWebshopItem on its items. The filter belongs in the query that reads the ERP:
SELECT id, itemid, upccode, displayname
FROM item
WHERE isonline = 'T'
AND isinactive = 'F'
A worked example. A distributor's NetSuite account has 620,000 item records: 318,000 inactive, 265,000 active but not marked for the web store, and 37,000 with isonline checked.
Sent without a filter, all 620,000 become Shopify variants. Shopify's resource limit applies once a store has 500,000 variants: from then on at most 10,000 new variants a day can be created, on any API, on plans below Plus. The last 120,000 items take 120,000 / 10,000 = 12 days, and every product created in those 12 days waits in the queue. With the filter, 37,000 items go over and the limit never comes into play. The same arithmetic applies to every later run: a nightly price check over 37,000 items instead of 620,000 is 6% of the reads. How to work out a run's duration is on integration throughput.
Historical orders stay where they are
Orders closed before go-live are already invoiced, paid and posted in the old system. Copying them into the new store or ERP adds no information and creates three problems.
| Order state at go-live | What happens to it |
|---|---|
| Open, not shipped | Moved: someone still picks and ships it |
| Shipped, not invoiced | Moved, or invoiced in the old system before the switch |
| Closed before go-live | Stays in the source, read-only and searchable |
First, the store side. Shopify's orderCreate mutation is documented for importing orders from an external system. Its defaults are set for that: inventoryBehaviour is BYPASS, so no stock is claimed, and sendReceipt and sendFulfillmentReceipt are false. A historical order is still a new order on the day it is created, so every app subscribed to new orders receives it as one. On a development or trial store, where an import is tested first, orderCreate is limited to five new orders a minute. Three years at 52,000 orders a year is 156,000 orders: 156,000 / 5 = 31,200 minutes, or 21 days and 16 hours for one test run.
Second, reading them back. An app reads orders from the last 60 days by default; older orders need the read_all_orders scope, which Shopify approves on request. Most integrations never need it, because they only handle orders from go-live onward.
Third, the ERP side. A NetSuite sales order that is created without a trandate gets today's date, so an imported 2024 order posts revenue in the current period without an error. The rules for the switch itself, including orders placed during it, are on replacing an integration.
Values one system calculates
Some fields are not data but results. Each has one system that calculates it, and it moves one way or not at all.
- Tax. Shopify calculates tax at checkout. If the ERP recalculates the same order with its own rates and rounding, the two totals differ by a cent on some orders and the payout no longer matches the invoice. The integration sends Shopify's tax lines as amounts and the ERP posts them as given. The details are in money, tax and rounding.
- Available stock. NetSuite derives
quantityavailablefrom on hand minus committed. It is read from NetSuite and written to the store; the store's own count is never written back as a new on-hand value. Which number to send is on which stock counts. - Cost.
averagecostandlastpurchasepriceare calculated by NetSuite from receipts. They can go to the store for margin reports, never the other way.
A value written into the system that calculates it is overwritten on the next calculation, or it overwrites the calculation. Both look like a sync working until someone compares the numbers.
Data that should not leave its system
The ERP gets what reconciliation and fulfillment need. It does not get what it cannot use.
| Data | Where it stays | What crosses instead |
|---|---|---|
| Card number | Payment gateway | Gateway, transaction ID, amount, card brand |
| Customer password or login code | Store | Nothing |
| Marketing consent, browsing data | Store | Nothing |
| Full order history per customer | Each system for its own orders | Customer key |
Shopify enforces the first row itself: CardPaymentDetails.number comes back with most of the leading digits redacted, and the bin field holds only the first few digits that identify the issuer. The other rows are a choice. A warehouse system needs the ship-to address and a phone number for the carrier, not the customer's newsletter status. Every personal field that crosses is one more copy to secure, to correct and to delete on request.
Content the store owns
Product descriptions, SEO titles, images and collections are written and checked in the store. They come from the ERP or PIM once, at the first load, and from then on the store owns them. The ERP keeps sending price, stock, barcode and weight.
The reason is concrete in Shopify's productSet mutation, which Shopify documents for syncing product data from an external source. For list fields such as collections, metafields and variants, it creates, updates and deletes: entries that are not in the input are removed. An integration that sends the ERP's category as the only collection on every run takes the product out of every collection the merchandiser added by hand. The fix is to leave collections out of the input, which leaves the field unchanged. More on what crosses for products is on field mapping.
What this means for your integration
We write down, with you, which data moves, in which direction and from which system, before anything is built. Inactive items, closed orders and calculated values are named in that list as staying where they are, so the decision is recorded instead of left to whoever builds the next flow. The integration then reads only what the list names.
A dry run shows per record what would change in the store or ERP before anything is written, which is where a filter that lets 620,000 items through gets caught. The data flows themselves are described on integrations. Tell us which systems you run, and you get the list of what we would sync and what we would leave.
Frequently asked questions
Should we import our old orders into the new store?
Usually not. Orders closed before go-live stay in the old store or ERP, where they are already posted and can still be looked up. Orders that are open at go-live, not yet shipped or not yet invoiced, are moved, because someone still has to act on them.
Is it safer to sync every field in case we need it later?
No. Each field is a mapping rule with its own validation and its own way to break when an API version changes. A field added later costs one rule and one backfill run; a field synced without an owner gets overwritten in both directions and nobody can tell which value is right.
Can we sync product descriptions from the ERP?
For the first load, yes. After that the description, SEO fields, images and collections belong to the store, because that is where they are written and checked. The ERP keeps sending price, stock, barcode and weight. Sending descriptions on every run overwrites the edits made in the store.
Does the ERP need the customer's card details?
No, and Shopify does not hand them out: its API returns the card number with most leading digits redacted. The ERP gets the payment method, the amount, the gateway and the transaction ID, which is what reconciliation against the payout needs.
What about customers who never ordered online?
They stay in the ERP until they order or until you open a B2B portal for them. Sending 40,000 ERP customers to a store that will see 3,000 of them creates 37,000 accounts to keep in sync and a set of welcome emails nobody asked for.
Tell us what you need
Tell us which systems you use and what needs to work better. We reply personally within one working day. We help you define the work, then agree the price and timing before you commit.
- You know the price and the timeline before you commit to anything
- We work out the connection that takes the work off your hands
- We handle the build, hosting, monitoring and maintenance
- Your team has less information to retype and fewer discrepancies to chase