Skip to content
SystemSyncer
Menu

ERP integration · Shopify + NetSuite

Shopify NetSuite Integration: Architecture, APIs, and Pitfalls

How to integrate Shopify with Oracle NetSuite — REST web services, OAuth 2.0, external-ID upserts, concurrency limits, order and inventory flows, and connector options.

Last reviewed:
Reading time
7 min read

Short answer

Shopify integrates with NetSuite through Shopify's Admin GraphQL API and webhooks on one side and NetSuite's SuiteTalk REST web services on the other. Oracle says new integrations should use REST with OAuth 2.0 — SOAP web services are being removed with the 2028.2 release. Use external-ID upserts (PUT …/eid:{id}) so retries never create duplicate sales orders, respect the account-wide concurrency limit shared with every other integration, and reconcile on a schedule. Oracle's NetSuite Connector and third-party iPaaS tools cover standard flows; custom code fits non-standard processes.

NetSuite integration surface at a glance (documented by the vendor; see sources)

Recommended API
SuiteTalk REST web services with OAuth 2.0 for new integrations (Oracle SOAP removal FAQ).
SOAP timeline
2025.2 is the last planned SOAP endpoint; new SOAP/TBA integrations can't be built from 2027.1; SOAP is removed with 2028.2.
Upsert
PUT /services/rest/record/v1/{record}/eid:{externalId} creates the record if missing, updates it if present.
Queries
SuiteQL via POST /services/rest/query/v1/suiteql (Prefer: transient header); max 100,000 results.
Concurrency
One account-wide limit shared by SOAP, REST, and RESTlets; based on service tier, +10 per SuiteCloud Plus license.
Limit exceeded
REST returns HTTP 429 with error code CONCURRENCY_LIMIT_EXCEEDED; Oracle says to retry.
Packaged connector
Oracle's NetSuite Connector (formerly FarApp) supports Shopify; added through your NetSuite account manager.
Data flow Typical Shopify–NetSuite data flows
  1. Shopify Orders
  2. Integration layer Queue
  3. NetSuite Sales orders

NetSuite is one of the most common ERPs behind mid-market Shopify and Shopify Plus stores in the US, and the pairing is well served by packaged connectors. It is also where many integrations go wrong: duplicate sales orders, stock that drifts between channels, and payouts nobody can reconcile. This page covers the integration surface on both sides and the decisions that determine whether the sync stays trustworthy. As on every page, documented marks statements from Oracle or Shopify documentation, and recommendation marks our guidance.

How Shopify and NetSuite connect

Documented (Shopify): Shopify exposes store data through the Admin GraphQL API and notifies external systems through webhooks such as order creation and updates. API calls are rate limited per app and store.

Documented (NetSuite): NetSuite offers SuiteTalk REST web services for records and SuiteQL queries, SOAP web services, and SuiteScript RESTlets (custom endpoints you write in NetSuite). Oracle’s SOAP removal FAQ states that newly built integrations should use REST web services with OAuth 2.0. From the 2027.1 release, new integrations can’t use token-based authentication (TBA), which SOAP requires, so new SOAP integrations can’t be built. The 2025.2 SOAP endpoint is the last planned one, and SOAP web services are removed with the 2028.2 release.

Recommendation: build new integrations on REST web services with OAuth 2.0. Use RESTlets only for logic REST can’t express well — for example, creating an order and its customer deposit in one server-side transaction. If you are inheriting a SOAP-based connector, put its migration on the roadmap now rather than in 2028.

A typical production architecture has three parts: Shopify webhooks land in a small receiver that verifies the HMAC signature and queues the event; a worker fetches the full order from Shopify, maps it, and upserts it into NetSuite; and a scheduled job reconciles both systems to catch anything the event path missed. See Shopify ERP integration architecture for the general pattern.

Choosing the NetSuite transaction model

Before you map a single field, decide how a Shopify order should look in NetSuite. This is an accounting decision, so involve the controller.

Shopify event Common NetSuite representation When it fits
Paid order Sales order (+ customer deposit for the payment) Warehouse picks from NetSuite; partial shipments; billing on fulfillment
Paid order Cash sale Simple flows where payment and sale are recorded together
Fulfillment Item fulfillment against the sales order NetSuite or a 3PL ships, tracking goes back to Shopify
Refund Credit memo + customer refund, or cash refund Depends on whether the original was invoiced or a cash sale
Return Return authorization → item receipt → refund When returned stock must be received and inspected

Recommendation: sales orders are the safer default for anything shipped from a warehouse, because they let fulfillment and billing happen when the goods actually leave. Whatever you choose, keep it consistent across channels — a store that creates cash sales while the B2B channel creates sales orders produces reports nobody trusts.

Order synchronization without duplicates

Documented: NetSuite REST supports an upsert operation. A PUT to a record URL that uses an external ID — for example /services/rest/record/v1/salesOrder/eid:SHOP-5551234 — adds the record if it doesn’t exist and updates it if it does. External IDs in the URL use the eid: prefix. Each record holds one external ID, and Oracle advises that a single integrated application should own the external ID values for a record type.

Documented: Shopify webhooks can be delivered more than once and are not guaranteed to arrive in order.

Recommendation:

  1. Derive the external ID from the Shopify order ID, with a prefix per store (SHOP-US-…, SHOP-CA-…) if you run several stores into one NetSuite account.
  2. Always create orders with the upsert, never with a plain POST. A timeout followed by a retry then can’t produce a second order.
  3. Record processed webhook IDs in your own store and skip repeats before calling NetSuite at all.
  4. Treat a timeout as “unknown”, not “failed”: read the record by external ID before deciding what to do next.
PUT https://{accountId}.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder/eid:SHOP-US-5551234
Authorization: Bearer {oauth2_access_token}
Content-Type: application/json

{
  "entity": { "id": "1842" },
  "otherRefNum": "#1043",
  "item": { "items": [
    { "item": { "id": "311" }, "quantity": 2, "rate": 24.50 }
  ] }
}

Illustrative only. Required fields, sublist structure, and custom fields depend on your account configuration — use the REST API Browser for your account to confirm the exact schema.

For the general technique, see preventing duplicate orders and idempotency.

Customers, items, and SKUs

Recommendation:

  • Items: map Shopify variants to NetSuite items by SKU (NetSuite’s item name/number or a dedicated custom field). Store the NetSuite internal ID against each variant once it’s matched, so later syncs don’t depend on string matching. Enforce unique, non-empty SKUs in Shopify before launch — see mapping products to ERP SKUs.
  • Customers: many US D2C stores post web orders to a single generic customer per channel to keep the customer list clean, while B2B buyers get individual customer records matched on a stable ID, not on email. Decide this up front; changing it later breaks reporting.
  • Kits and bundles: a Shopify bundle often maps to a NetSuite kit or assembly. Decide which system explodes the bundle into components for fulfillment and inventory.

Inventory synchronization

Documented (Shopify): Shopify tracks inventory per location and exposes quantity states through the Admin API.

Recommendation:

  • Make NetSuite the source of truth for on-hand quantity and publish available-to-sell quantities to Shopify per location, minus a safety buffer for fast sellers.
  • Map each Shopify location to a NetSuite location explicitly; don’t sum warehouses into one number unless the store really ships from all of them.
  • Push changes on events (receipts, adjustments, fulfillments) and run a full reconciliation daily — a SuiteQL query of quantities by item and location compared with Shopify levels. Oracle documents a maximum of 100,000 results per SuiteQL query, so page through large catalogs.
  • Account for orders that exist in Shopify but are not yet in NetSuite when computing what to publish, or you will oversell during sync lag.

See syncing inventory between Shopify and an ERP and inventory reconciliation.

Fulfillment and tracking

Recommendation: when NetSuite (or a 3PL integrated with NetSuite) ships, create the item fulfillment in NetSuite first, then create the fulfillment with tracking in Shopify, which notifies the customer. Carry the Shopify fulfillment order ID through the flow so partial shipments land on the right lines. For US carriers, store the carrier name in the format Shopify recognizes (UPS, FedEx, USPS) so tracking links work. See syncing shipment tracking.

Sales tax, payments, and payouts

Recommendation:

  • Sales tax: decide which system calculates tax. Most stores let Shopify (or a tax engine such as Avalara connected to it) calculate tax at checkout and post the calculated amounts to NetSuite rather than recalculating. Recalculating in NetSuite produces penny differences and, worse, different answers on exempt B2B customers. Have your tax advisor confirm the setup for each state where you have nexus. See sales tax in ERP integrations.
  • Payments: record the payment against the order (for example as a customer deposit) using the gateway and transaction reference, so finance can match it.
  • Payouts: a Shopify Payments payout is a net amount — sales minus refunds, fees, and adjustments. Post each payout as a deposit that clears the individual payments and books the processing fees, instead of trying to match payout totals to orders by hand.

API limits and concurrency

Documented (NetSuite): concurrency is governed per account and the limit is shared by SOAP web services, REST web services, and RESTlets. The base limit depends on the account’s service tier, and each SuiteCloud Plus license raises it by 10. Oracle notes the limit is lenient and can be exceeded temporarily. When a REST request is rejected, NetSuite returns HTTP 429 with error code CONCURRENCY_LIMIT_EXCEEDED, and Oracle’s guidance is to retry. Administrators can review and assign limits per integration in the integration governance settings.

Documented (Shopify): Shopify’s Admin API applies its own rate limits per app and store.

Recommendation:

  • Size your worker pool to a fraction of the account limit — other integrations (3PL, tax, CRM, payroll) share the same pool.
  • Ask the NetSuite administrator to assign a per-integration limit, so a backlog catch-up can’t block the warehouse integration.
  • Retry 429s with exponential backoff and jitter; send non-retryable validation errors to a dead-letter queue for a human.
  • Run bulk catch-ups (historical orders, full catalog loads) outside US business hours.

Connector vs. custom integration

Documented: Oracle’s NetSuite Connector (formerly FarApp) supports Shopify as a storefront. Oracle’s setup guide says the Shopify connector is added to your account through your NetSuite account manager, then authorized from the connector’s settings.

Recommendation:

  • A packaged connector (Oracle’s, or an iPaaS template from vendors such as Celigo) fits when your flows are standard: orders in, fulfillments and inventory out, typical refunds.
  • A custom integration fits when the process is not standard: complex bundles, multiple subsidiaries with intercompany flows, B2B pricing rules, or a 3PL in the middle.
  • Hybrid is common: a connector for the standard flows plus custom code (often a RESTlet or a small service) for the one process the connector can’t handle.

Compare the trade-offs in custom integration vs. off-the-shelf connectors, and estimate effort with the ERP integration cost estimator.

Common pitfalls

  • Duplicate sales orders from retries that used POST instead of an external-ID upsert.
  • Concurrency starvation: one integration’s backlog consuming the whole account limit.
  • Tax mismatches from recalculating tax in NetSuite instead of posting Shopify’s amounts.
  • Unmatched payouts because fees and refunds were never booked separately.
  • SKU drift: variants created in Shopify that don’t exist in NetSuite, causing orders to fail mapping.
  • New SOAP or TBA work that will need to be rebuilt before the 2028.2 removal.

Frequently asked questions

Does NetSuite have a native Shopify integration?

Oracle offers NetSuite Connector (formerly FarApp), which supports Shopify as a storefront. According to Oracle's documentation, the Shopify connector is added to your account through your NetSuite account manager and then authorized against your myshopify.com store. Third-party iPaaS vendors also sell Shopify–NetSuite connectors. Whether a packaged connector fits depends on how standard your order, tax, and fulfillment processes are.

Should a new Shopify–NetSuite integration use SOAP or REST?

REST. Oracle's SOAP removal FAQ says newly built integrations should use REST web services with OAuth 2.0, that new integrations using token-based authentication (required by SOAP) can't be built starting with the 2027.1 release, and that SOAP web services are removed with the 2028.2 release. Where REST web services can't cover a scenario, Oracle points to SuiteScript RESTlets.

How do I stop the integration from creating duplicate sales orders in NetSuite?

Use the Shopify order ID (or a value derived from it) as the NetSuite external ID and create orders with an upsert — a PUT to the record URL with eid:{externalId}. A retry then updates the existing order instead of creating a second one. Also keep your own processed-events table, because Shopify can deliver the same webhook more than once.

Why does my integration get HTTP 429 errors from NetSuite?

NetSuite enforces a concurrency limit per account, shared by SOAP, REST, and RESTlet traffic from every integration. When the limit is exceeded, REST requests return 429 with CONCURRENCY_LIMIT_EXCEEDED. Lower your parallelism, retry with exponential backoff, and ask your administrator to assign per-integration limits so one integration can't starve the others. SuiteCloud Plus licenses raise the account limit.

Should Shopify orders become sales orders or cash sales in NetSuite?

That is an accounting and fulfillment decision, not an API one. Sales orders support a separate fulfillment and billing process, which suits warehouses and partial shipments; cash sales record a paid sale in one transaction. Agree on the transaction model with your controller before writing any mapping code.

Sources and further reading

Platform-specific statements on this page were checked against these sources on . Vendor APIs change; confirm details in the current documentation before you build.

  1. [1] SOAP Removal Plans FAQ — Oracle NetSuite
  2. [2] Using the Upsert Operation (REST web services) — Oracle NetSuite
  3. [3] Using External IDs (REST web services) — Oracle NetSuite
  4. [4] Executing SuiteQL Queries Through REST Web Services — Oracle NetSuite
  5. [5] Error Handling in REST Web Services — Oracle NetSuite
  6. [6] Enabling Web Services Concurrent Users with SuiteCloud Plus — Oracle NetSuite
  7. [7] NetSuite Concurrency Limits — Oracle NetSuite
  8. [8] Integrating NetSuite Connector with Shopify — Oracle NetSuite
  9. [9] Shopify webhooks — Shopify
  10. [10] Shopify API rate limits — Shopify