How to Build a Bulletproof Shopify ↔ NetSuite Integration
A practical, step-by-step build guide that turns the architecture into a real, resilient integration. Each step says exactly what to wire up and links to the official Shopify and NetSuite documentation so you can verify everything for yourself.
Practical build guide · Backed by official Shopify & NetSuite documentation
This is the practical companion to our Building a Bulletproof Shopify ↔ NetSuite Integration guide. The main article explains the architecture and the trade-offs; this page is the plain checklist for actually building it. Work through the steps in order — the early decisions (system of record, the queue, idempotency) shape everything after — and use the official Shopify and NetSuite documentation link under every step to confirm the exact API or screen as you go.
Before you start — prerequisites
Access: a Shopify store (ideally Plus) with a custom app / Admin API access, and a NetSuite account with REST web services and OAuth 2.0 / token-based auth.
A system-of-record decision: who owns each entity, agreed and written down (that’s Step 1) before any code.
A clean SKU master and test environments: a reconciled SKU list, plus a Shopify development store and a NetSuite Sandbox — never build against production.
What you'll set up
Map what syncs & pick a system of record.
Stand up the middleware (queue + retry + dead-letter).
Wire the order flow (webhook → queue → RESTlet).
Make every endpoint idempotent.
Reconcile SKUs before go-live.
Build the scheduled inventory flow.
Write fulfillment, refunds & returns back.
Choose connector vs. custom.
Add monitoring, dead-letter recovery & alerts.
1
Map what syncs & pick a system of record
Before any code: list every entity that flows between the two systems, its direction, and which system is authoritative. One boss per entity; the other side gets a read-only copy.
Where: a one-page decision doc in source control
List the entities: products/variants, inventory, pricing, customers, orders, fulfilments, refunds, returns, tax/shipping — with the sync direction for each.
Default that works for most brands: NetSuite owns items, pricing, inventory, customer master, and accounting; Shopify owns storefront, sessions, and order origination.
Document the exceptions explicitly — e.g. a negotiated B2B price on a Shopify Plus order is the system of record for that order, not the catalog.
Stand up the middleware (queue + retry + dead-letter)
The middleware layer is non-negotiable for production. Put a durable queue between Shopify and NetSuite so spikes and outages never drop an order.
Where: your integration service (e.g. AWS Lambda + SQS)
Build an event-driven service whose job is to absorb spikes, retry with exponential backoff, de-duplicate webhook replays, and route events to the right RESTlet.
Authenticate to NetSuite with REST web services + OAuth 2.0 / token-based auth; never point Shopify webhooks straight at a RESTlet.
Configure a dead-letter queue: after N retries (5 is typical) a failed message parks there and pages an on-call engineer.
Subscribe to orders/paid; verify the HMAC signature in the headers and reject anything that doesn’t match.
Enqueue the event and return 200 OK within 5 seconds (Shopify times out webhooks after 5s and marks them failed).
A worker calls a NetSuite RESTlet that runs the idempotency check (Step 4), resolves the customer, maps SKUs to item internal IDs, and creates the Sales Order.
On failure, the message returns to the queue for backed-off retry, then to the dead-letter queue.
The single rule that prevents duplicates: calling an endpoint twice with the same Shopify ID must produce the same result as calling it once.
Where: every record-creating RESTlet
Store the Shopify entity ID in an indexed custom field on the NetSuite record (custbody_shopify_order_id, custentity_shopify_customer_id).
Every RESTlet searches for an existing record with that ID before creating one; if found, return the existing internal ID as success — don’t throw, don’t duplicate.
Enforce the uniqueness in code at the search level (NetSuite doesn’t enforce DB uniqueness on custom fields).
Be honest about which flows need custom code. Most production stacks are a connector for the standard 80% plus custom code for the 20% that doesn’t fit.
Where: an architecture decision, per flow
Connector (Celigo, NetSuite Connector / FarApp, Boomi) when: under ~500 orders/day, simple SKUs, single store/subsidiary/location, B2C only, speed-to-live matters most.
Custom when: high or spiky volume, kits/bundles/configurable products, multi-store or multi-subsidiary routing, B2B negotiated pricing, or you need full control of retries and audit logs.
If you split 80/20, make the two halves aware of each other — shared SKU master, shared idempotency keys — or they’ll fight.
If a finance person can’t answer “did the last 24 hours sync cleanly?” from one screen, you’re not done. Surface failures; don’t bury them in logs.
Where: dashboards, dead-letter queue, alerting
Dashboard sync counts by status and end-to-end latency; alert on dead-letter depth and on SKU-reconciliation drift.
Give the dead-letter queue a one-click retry UI so a human can replay a fixed message — don’t make failures a code deploy.
Watch NetSuite governance (RESTlet and Map/Reduce usage units) and alert before you hit limits.
Avoid hardcoded location/subsidiary IDs — they break in Sandbox and the first time someone adds a fulfilment location. Replay last year’s peak before BFCM.
A note on accuracy: Shopify and NetSuite ship API changes on a fixed cadence. Pin a Shopify Admin API version, prefer NetSuite REST web services with OAuth 2.0 (SOAP is being retired), verify every webhook’s HMAC signature, and validate every flow in a Shopify development store and a NetSuite Sandbox. Replay last year’s peak hour against staging before BFCM — the integration that handles a quiet Tuesday has never seen Black Friday.
A connector gets a simple, low-volume D2C store syncing quickly. What sits beyond point-and-click — the middleware queue, strict idempotency, the reconciliation tooling, a dead-letter queue with a retry UI, the unmapped-SKU fallback handler, B2B pricing, and multi-location/multi-subsidiary routing — is real engineering. Most production stacks run a connector for the standard 80% of flows and custom workers for the irreducible 20%, with a shared SKU master and shared idempotency keys so the two halves don’t fight.
The queue, the idempotency check, and the reconciler are the parts that decide whether the integration survives peak season, so build and load-test them first. For the full background on each decision — system of record, the reference architecture, and the failure modes — read the companion guide, and switch it to Explain it simply mode if you want the plain-English version first.
Integration dropping orders, double-creating customers, or drifting inventory?Our engineers review your Shopify-NetSuite stack — connector or custom — and map exactly which flows to keep, which to rebuild, and what to add (queue, idempotency, reconciliation, monitoring) before your next peak season.
Documentation links point to Shopify’s official developer docs (shopify.dev) and the Oracle NetSuite Help Center (docs.oracle.com). API versions, record names, and menu paths change between releases — pin your Shopify API version, prefer NetSuite REST with OAuth 2.0, and validate in a development store and Sandbox first.