Every Shopify store that outgrows spreadsheets hits the same wall. Orders pile up in Shopify, finance lives in NetSuite, and someone is stuck copying data between the two. 😅
Sooner or later, the question becomes: how do orders, inventory, fulfillments, and refunds move between these systems without a human in the middle?
There are three realistic routes: Celigo, Boomi, or a custom build on the APIs. I compared all three against the same checklist. The answer wasn't what I expected, and the deciding factor had nothing to do with setup speed.
TL;DR ⚡
- Celigo is the fastest route to a working sync for standard flows.
- Boomi makes sense when Shopify and NetSuite are two of many systems you need to connect.
- A custom API build wins only when your business logic is truly unusual, and you have a team to maintain it for years.
- A Shopify NetSuite integration service is only as good as its error handling after launch, not its setup speed.
- Whichever route you pick, error handling and edge cases decide whether the integration survives, not the happy path.
What I needed to sync 📋
Before comparing tools, I wrote down the actual data flows, because "integrate Shopify and NetSuite" hides a lot of work.
Shopify to NetSuite
- Orders become sales orders (or cash sales, depending on payment flow)
- Customers become NetSuite customer records.
- Refunds and returns become credit memos or return authorizations.
- Payouts and fees get reconciled for accounting.
NetSuite to Shopify
- Item fulfillments with tracking numbers
- Inventory levels per location
- Product and price updates
- Order status changes
Most teams underestimate the second list. Getting orders into NetSuite feels like the finish line, but the return trip is where customer experience and inventory accuracy live.
How I judged them ⚖️
Setup speed is the criterion everyone leads with, and it matters least over a multi-year horizon. These are the criteria I actually weighed:
- Time to first working sync
- Total cost over 2 to 3 years, including people time, not just license fees
- Error handling and visibility (who finds out when a record fails, and how fast)
- Flexibility for custom fields, unusual tax rules, and bundles
- Maintenance burden after launch
- Who can operate it (developers only, or ops and finance too)
Option 1: Celigo 🟢
Celigo offers a prebuilt integration app for Shopify and NetSuite, so the common flows are already mapped. You configure it instead of building it from scratch.
What worked well:
- Fastest path to a working sync, because the standard order, fulfillment, and inventory flows exist out of the box
- A visible error dashboard where failed records can be inspected and retried by ops staff, no developer ticket required
- Field mappings can be viewed and edited in the interface, which makes audits easier.
- Pre-built handling for several common edge cases, so you inherit lessons other customers already paid for
Where it struggled:
- Heavy customization means working inside the flow builder, and complex branching gets fiddly fast.
- Costs are recurring and typically scale with the number of flows and endpoints, so check current pricing against your growth plans.
- You depend on the vendor's roadmap for new Shopify and NetSuite API changes.
Best fit: teams with standard order-to-cash flows who want to be live quickly and want ops to own day-to-day monitoring.
Option 2: Boomi 🟡
Boomi is a general-purpose integration platform (iPaaS), not a Shopify NetSuite product. It provides connectors for both systems, and you design the processes, mappings, and error routes yourself.
What worked well:
- Strong governance, monitoring, and environment management, which larger organizations care about
- Excellent when Shopify and NetSuite are only part of a bigger landscape (a WMS, a 3PL, a marketplace, a CRM)
- Reusable components, so the second and third integrations get cheaper
Where it struggled:
- More build effort than Celigo for the same Shopify NetSuite result, because you are assembling flows instead of configuring a packaged app
- Overkill if you only connect two systems and have no plans to add more
- Needs someone who actually knows the platform, which is a real hiring and training cost
Best fit: mid-size and enterprise teams with a multi-system integration strategy and in-house or partner expertise on the platform.
Option 3: Custom API build 🔴
This means Shopify webhooks and the GraphQL Admin API on one side, NetSuite RESTlets or REST web services on the other, and a queue in the middle to absorb bursts and retries.
A typical architecture looks like this:
What worked well:
- Total control over mapping, logic, and edge cases
- No platform license fee
- You can model unusual rules exactly (custom bundles, split shipments, special tax handling)
Where it struggled:
- You own everything: auth, retries, rate limits, monitoring, alerting, and API version upgrades.
- The first version always looks simple. The tenth edge case is where the weeks go.
- When the developer who built it leaves, the integration becomes a liability.
The core pieces you must build yourself
1. Verify and acknowledge webhooks fast: Shopify signs each webhook, so verify it before doing anything else. Do the heavy work later, in a queue.
import crypto from "crypto";
import express from "express";
const app = express();
app.post(
"/webhooks/shopify/orders-create",
express.raw({ type: "application/json" }),
async (req, res) => {
const received = req.get("X-Shopify-Hmac-Sha256") || "";
const expected = crypto
.createHmac("sha256", process.env.SHOPIFY_API_SECRET)
.update(req.body)
.digest("base64");
const valid =
received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return res.sendStatus(401);
const webhookId = req.get("X-Shopify-Webhook-Id");
if (await alreadyProcessed(webhookId)) return res.sendStatus(200);
await queue.add("create-netsuite-sales-order", {
webhookId,
order: JSON.parse(req.body.toString("utf8")),
});
res.sendStatus(200); // respond quickly, process async
}
);
2. Make every write idempotent: Webhooks can be delivered more than once, and your worker can retry after a timeout. Use the Shopify order ID as the NetSuite external ID so a second attempt updates or rejects instead of creating a duplicate sales order.
async function upsertSalesOrder(order) {
const externalId = `shopify-${order.id}`;
const existing = await netsuite.findSalesOrderByExternalId(externalId);
if (existing) return existing; // already created, safe to skip
return netsuite.createSalesOrder({
externalId,
entity: await resolveCustomer(order.customer),
items: await mapLineItems(order.line_items),
// ...taxes, shipping, discounts, currency
});
}
3. Retry with backoff, then park failures: Transient errors (timeouts, throttling) should retry with increasing delays. Permanent errors (an unmapped SKU) should go to a dead-letter queue where a human can fix the data and replay the record.
const RETRYABLE = new Set([429, 502, 503, 504]);
async function withRetry(fn, { attempts = 5, baseMs = 500 } = {}) {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
const retryable = RETRYABLE.has(err.status) || err.code === "ETIMEDOUT";
if (!retryable || i === attempts - 1) throw err;
const delay = baseMs * 2 ** i + Math.random() * 250;
await new Promise((r) => setTimeout(r, delay));
}
}
}
Notice what is missing from those snippets: monitoring, alerting, a replay UI, token refresh, and reconciliation reports. Those are the parts that quietly turn a two-week build into a two-quarter project.
Side by side 📊
| Criteria | Celigo | Boomi | Custom API |
|---|---|---|---|
| Setup speed | Fast | Medium | Slow |
| Upfront cost | Low | Medium | High (dev time) |
| Ongoing cost | Subscription | Subscription | Maintenance time |
| Flexibility | Medium | High | Highest |
| Error handling | Built in | Built in | You build it |
| Ops can monitor it | Yes | Yes (with training) | Only if you build a UI |
| Handles API changes | Vendor | Vendor | You |
| Best for | Standard flows | Many systems | Unusual logic |
The problems that hit every route 🚧
This is the part I wish someone had told me up front. Happy-path syncing works on all three options. These issues show up no matter which one you choose; the difference is who has to solve them.
- Duplicate and out-of-order events: Webhooks can arrive twice or out of order. An "order updated" event can land before "order created." Without idempotency and ordering logic, you get duplicate orders or stale data overwriting fresh data.
- API throttling on both sides: Shopify's GraphQL Admin API uses cost-based rate limits, and NetSuite limits concurrent requests per account. A flash sale or a big inventory push can hit both. Queues and backoff are not optional.
- Refunds, partial fulfillments, and post-checkout edits: Order creation is the easy event. Partial refunds, split shipments, exchanges, and edited orders are where integrations drift out of sync, and finance notices at month-end.
- SKU and item matching: If Shopify variants and NetSuite items are matched on inconsistent fields, you either fail to map or map to the wrong item. Pick one canonical key and enforce it on both sides before any integration goes live.
- Customer matching: Matching on email alone creates duplicates when customers use different addresses or guest checkout. Decide your matching rules deliberately.
- Taxes, discounts, and shipping: NetSuite and Shopify can calculate these differently. Small rounding or allocation differences multiply across thousands of orders and become reconciliation headaches.
- Inventory race conditions: If two systems both think they own stock counts, you oversell. Decide which system is the source of truth for inventory and make the other one read-only.
- API version changes: Shopify retires old API versions on a regular schedule, so a custom build needs someone assigned to upgrades. Platforms like Celigo and Boomi absorb much of this for you.
Thinking about real cost 💰
License fees are the number everyone compares, but they are rarely the biggest line item. When you model total cost over two or three years, include:
- Build or configuration time for the first version.
- Ongoing maintenance: API upgrades, bug fixes, new flows when the business changes
- Incident time: who gets paged and how long recovery takes when a sync fails
- Opportunity cost: what else your developers could have built
- Risk: what a week of broken order sync costs the business
A custom build looks cheap on paper because there is no subscription. It often becomes the most expensive option once the maintenance and incident time are counted honestly. A platform looks expensive until you price the engineer-weeks it replaces.
A simple decision guide 🧭
Ask these questions in order:
- Are your flows mostly standard (orders, fulfillments, inventory, refunds)? If yes, start by evaluating Celigo.
- Do you need to connect many systems, not just Shopify and NetSuite? If yes, evaluate Boomi or another iPaaS.
- Is there logic no connector can model, and do you have engineers who will own it long term? Only then does a custom build make sense.
- Who will monitor this day-to-day? If the answer is "ops and finance, not developers," favor a platform with a real dashboard.
A path that worked: start on a platform, go custom only where needed 🛤️
Hybrid setups are underrated. Run the standard flows on a platform and build custom services only for the pieces that genuinely need them, such as a special pricing engine or a bundle-handling service the platform calls out to. You get speed and visibility for 90 percent of the work, and control where it counts.
Moving from a platform to custom later is straightforward. Rescuing a custom build that nobody wants to maintain is not.
Mistakes to avoid ⚠️
- Choosing based on setup speed alone
- Skipping a sandbox test with refunds, partial shipments, and multi-line discounts
- Not deciding the source of truth for inventory and customers before building.
- Treating monitoring and alerting as phase two
- Forgetting that finance, not just developers, has to trust the numbers.
- Not testing month-end close with real integration data.
Pre-launch checklist ✅
- Source of truth defined for orders, inventory, customers, and prices
- Canonical SKU/item key enforced in both systems
- Idempotency in place so retries never create duplicates.
- Failed records visible and replayable by a non-developer
- Alerts configured for sync failures and growing queue depth.
- Refunds, partial fulfillments, and edited orders tested end to end
- Multi-currency and tax behavior verified against finance expectations.
- An owner named for API version upgrades
- Reconciliation report that compares Shopify totals to NetSuite totals
My verdict 🏁
- Choose Celigo if your flows are fairly standard and you want to live quickly with a dashboard that ops can use.
- Choose Boomi if Shopify and NetSuite are part of a bigger integration landscape and you have the expertise to run the platform.
- Choose a custom API build if your business logic is genuinely unusual and you have a team committed to maintaining it long term.
If you are unsure, start with a platform. The time you save on retries, monitoring, and API upgrades is time you can spend on the parts of the business that actually differentiate you.
Your turn 👇
What's the best Shopify tip you've picked up from running an ERP integration? Drop it in the comments. Also tell me which route you picked (Celigo, Boomi, or custom) and what broke first. I'll share how I'd approach your setup.

Top comments (7)
The point about finance noticing at month-end is the one that matters most. A sync can show “success” on every record and still drift, because partial refunds and rounding differences don’t throw errors. A daily job that compares Shopify totals to NetSuite totals, with a small tolerance, catches that drift in a day, not at close.
I’d also run that report during the first two weeks after launch, before trusting any dashboard. Did you run reconciliation as a separate check, or rely on the built-in error handling? 🤔
Exactly, and that’s why the reconciliation report is on my pre-launch checklist. Built-in error handling only catches records that fail. It won’t catch partial refunds or rounding that “succeed” but come out slightly wrong. I treat the daily totals comparison as its own check, separate from the dashboard, and keep it running through the first month-end close. Do you use a tolerance threshold, or flag any difference? 📊
The hybrid approach is underrated, and it works best when the boundary is drawn early. Keep the standard order, fulfillment, and inventory flows on the platform, and give custom code only the one thing it can’t model, like a bundle or pricing rule. That way the platform’s dashboard still covers 90 percent of failures.
The risk is a custom service with no alerting of its own, so failures there stay invisible while the platform dashboard looks green. Making sure the custom piece reports into the same monitoring is worth planning from day one 📊
Agreed. The boundary is the whole game, and the monitoring point is the one people miss. A custom service that fails quietly while the platform dashboard shows green is worse than having no dashboard, because it creates false confidence. I’d push the custom piece’s errors into the same alert channel from day one. Which piece would you carve out first, pricing or bundles? 🤔
For “what broke first,” my vote goes to SKU matching. It rarely fails loudly. A variant maps to the wrong item, orders still flow, and nobody notices until inventory counts stop making sense. Agreeing on one canonical key before the first sync saves a lot of cleanup later.
Inventory ownership is a close second. Once both systems can write stock counts, overselling is only a matter of time during a sale. Deciding upfront that one system is the source of truth and the other is read-only prevents most of it 🛒
Thanks for answering the question I asked! SKU mismatches are sneaky for exactly that reason: orders keep flowing, so nothing looks broken until inventory stops adding up. And agreed on stock ownership. Once two systems can both write counts, overselling during a sale is a matter of when. Choosing one source of truth and making the other read-only prevents most of it 🛒
Some comments may only be visible to logged-in visitors. Sign in to view all comments.