---
# === IDENTITY ===
id: business/erp-integration/ecommerce-to-erp/2026
canonical_question: "How do you integrate Shopify/Magento/BigCommerce with ERP for order and inventory sync?"
aliases:
  - "Ecommerce to ERP integration order sync inventory"
  - "Shopify NetSuite order sync integration pattern"
  - "Magento ERP inventory synchronization best practices"
  - "BigCommerce D365 SAP integration order fulfillment"
entity_type: erp_integration
domain: business > erp-integration > ecommerce-to-erp
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === SYSTEM PROFILE ===
systems:
  - name: "Shopify"
    vendor: "Shopify"
    version: "Admin API 2025-01"
    edition: "Basic / Shopify / Advanced / Plus"
    deployment: cloud
    api_surface: "REST Admin, GraphQL Admin, Webhooks, Bulk Operations"
  - name: "Adobe Commerce (Magento 2)"
    vendor: "Adobe"
    version: "2.4.7"
    edition: "Open Source / Commerce / Commerce Cloud"
    deployment: "cloud / on-premise"
    api_surface: "REST, GraphQL, SOAP, Message Queues"
  - name: "BigCommerce"
    vendor: "BigCommerce"
    version: "V3 REST API"
    edition: "Standard / Plus / Pro / Enterprise"
    deployment: cloud
    api_surface: "REST V2/V3, Webhooks, GraphQL (Storefront)"
  - name: "Oracle NetSuite"
    vendor: "Oracle"
    version: "2024.2"
    edition: "SuiteCloud Plus"
    deployment: cloud
    api_surface: "SuiteTalk SOAP, RESTlets, REST API, SuiteQL"
  - name: "SAP S/4HANA Cloud"
    vendor: "SAP"
    version: "2408"
    edition: "Public / Private Edition"
    deployment: cloud
    api_surface: "OData v4, SOAP, IDoc, BAPI"
  - name: "Microsoft Dynamics 365 Business Central"
    vendor: "Microsoft"
    version: "API v2.0"
    edition: "Essentials / Premium"
    deployment: cloud
    api_surface: "OData v4, REST API v2.0, Business Events"

# === VERIFICATION ===
last_verified: 2026-03-07
confidence: 0.85
version: 1.0
first_published: 2026-03-07

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: volatile
  last_breaking_change: "Shopify REST Admin API deprecation in favor of GraphQL (2025-01 version)"
  next_review: 2026-09-03
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "Shopify REST Admin API: 2 requests/second sustained (burst to 40 bucket); GraphQL: 100 points/second (Standard), up to 1,000/s (Plus) — shared across all apps on the store"
  - "BigCommerce V3 API: 150 requests/30s (Standard/Plus), 450 requests/30s (Pro), Enterprise varies — 429 with X-Rate-Limit-Time-Reset-Ms header"
  - "Magento 2 rate limiting is disabled by default — must configure via Admin > Stores > Configuration > Sales > Rate Limiting backed by Redis"
  - "NetSuite SuiteTalk: default 5 concurrent requests per account; governance units limit SuiteScript execution (1,000 units for scheduled scripts)"
  - "Inventory sync must be unidirectional (ERP → ecommerce) to prevent conflicts; bidirectional sync requires explicit conflict resolution and source-of-truth designation"
  - "Shopify webhook delivery: max 19 seconds response time before timeout; webhooks retry up to 19 times over 48 hours on failure"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Need single-system Shopify API reference only"
    use_instead: "Shopify official API docs at shopify.dev/docs/api"
  - condition: "Need single-system NetSuite API reference only"
    use_instead: "business/erp-integration/netsuite-suitetalk-api-capabilities/2026"
  - condition: "Need SAP S/4HANA OData API capabilities only"
    use_instead: "business/erp-integration/sap-s4hana-odata-api-capabilities/2026"
  - condition: "Need general order-to-cash across CRM+ERP (not ecommerce-specific)"
    use_instead: "business/erp-integration/order-to-cash-integration/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: ecommerce_platform
    question: "Which ecommerce platform are you integrating?"
    type: choice
    options:
      - "Shopify (Basic/Shopify/Advanced/Plus)"
      - "Adobe Commerce / Magento 2"
      - "BigCommerce"
      - "Multiple platforms"
  - key: erp_system
    question: "Which ERP system is the target?"
    type: choice
    options:
      - "Oracle NetSuite"
      - "SAP S/4HANA"
      - "Microsoft Dynamics 365 Business Central"
      - "Other"
  - key: integration_pattern
    question: "What integration pattern do you need?"
    type: choice
    options:
      - "real-time sync (individual orders, <1min latency)"
      - "near-real-time (webhook-driven, 1-5min)"
      - "batch/scheduled (every 15-60min, high volume)"
      - "event-driven (CDC, platform events)"
  - key: data_volume
    question: "What's your daily order volume?"
    type: choice
    options:
      - "< 100 orders/day"
      - "100-1,000 orders/day"
      - "1,000-10,000 orders/day"
      - "> 10,000 orders/day (flash sales, peak seasons)"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/business/erp-integration/ecommerce-to-erp/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-03-07)"

# === RELATED UNITS ===
related_kos:
  depends_on:
    - id: "business/erp-integration/batch-vs-realtime-integration/2026"
      label: "Batch vs Real-time Integration Patterns"
  related_to:
    - id: "business/erp-integration/order-to-cash-integration/2026"
      label: "Order-to-Cash Cross-System Integration"
    - id: "business/erp-integration/netsuite-suitetalk-api-capabilities/2026"
      label: "NetSuite SuiteTalk API Capabilities"
    - id: "business/erp-integration/idempotency-erp-integrations/2026"
      label: "Idempotency in ERP Integrations"
  solves:
    - id: "business/erp-integration/error-handling-dead-letter-queues/2026"
      label: "Error Handling and Dead Letter Queues"
  alternative_to:
    - id: "business/erp-integration/salesforce-netsuite-integration/2026"
      label: "Salesforce-NetSuite Integration (CRM-to-ERP)"
  often_confused_with:
    - id: "business/erp-integration/master-data-management-erp/2026"
      label: "Master Data Management (product/customer master sync vs transactional sync)"

# === SOURCES ===
sources:
  - id: src1
    title: "Shopify API Usage Limits"
    author: Shopify
    url: https://shopify.dev/docs/api/usage/limits
    type: official_docs
    published: 2025-01-01
    reliability: authoritative
  - id: src2
    title: "ERP Integration: What It Is and How It Works"
    author: Shopify
    url: https://www.shopify.com/enterprise/blog/erp-integration
    type: technical_blog
    published: 2025-06-15
    reliability: high
  - id: src3
    title: "NetSuite Shopify Integration: Complete Technical Guide"
    author: Houseblend
    url: https://www.houseblend.io/articles/netsuite-shopify-integration-technical-guide
    type: technical_blog
    published: 2025-09-01
    reliability: moderate_high
  - id: src4
    title: "BigCommerce API Rate Limits"
    author: BigCommerce
    url: https://developer.bigcommerce.com/docs/start/best-practices/api-rate-limits
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src5
    title: "Adobe Commerce Rate Limiting"
    author: Adobe
    url: https://developer.adobe.com/commerce/webapi/get-started/rate-limiting/
    type: official_docs
    published: 2025-03-01
    reliability: authoritative
  - id: src6
    title: "ERP Integrations: Common Methods + Best Practices"
    author: BigCommerce
    url: https://www.bigcommerce.com/articles/ecommerce/erp-integration/
    type: technical_blog
    published: 2025-08-01
    reliability: high
  - id: src7
    title: "Shopify ERP Integration Guide: Methods, Benefits, and Tools for 2026"
    author: Celigo
    url: https://www.celigo.com/blog/shopify-erp-integration/
    type: technical_blog
    published: 2025-11-01
    reliability: moderate_high
---

# Ecommerce-to-ERP Integration: Order & Inventory Sync for Shopify, Magento, and BigCommerce

## TL;DR

- **Bottom line**: Use webhook-driven near-real-time sync for orders (ecommerce → ERP) and scheduled batch sync for inventory (ERP → ecommerce); never use bidirectional inventory sync without explicit conflict resolution. [src2, src6]
- **Key limit**: Shopify GraphQL Admin API allows 100 points/second (Standard) up to 1,000/s (Plus); BigCommerce allows 150-450 requests/30s by plan; Magento rate limiting is off by default. [src1, src4, src5]
- **Watch out for**: SKU mapping mismatches between platforms — `BLK-SHIRT-L` in Shopify vs `BLACK_SHIRT_LARGE` in your ERP will silently break sync. [src3, src7]
- **Best for**: Retailers running 100-10,000 orders/day who need automated order flow into ERP with real-time inventory visibility back to the storefront. [src6]
- **Authentication**: Shopify uses OAuth 2.0 (custom apps) or access tokens (private apps); Magento uses OAuth 1.0a or bearer tokens; BigCommerce uses OAuth + API tokens; NetSuite uses Token-Based Authentication (TBA). [src1, src3, src5]

## System Profile

This integration playbook covers connecting the three dominant ecommerce platforms — Shopify, Adobe Commerce (Magento 2), and BigCommerce — to major ERP systems for automated order processing and inventory synchronization. The primary data flows are: orders from ecommerce to ERP (including customer data, line items, payment status, and shipping addresses), and inventory/fulfillment data from ERP back to ecommerce (stock levels, tracking numbers, shipment status). [src2, src6]

The playbook focuses on cloud-to-cloud integrations using native APIs and iPaaS middleware. On-premise Magento installations follow the same API patterns but require network/firewall configuration not covered here. For direct CRM-to-ERP integration (e.g., Salesforce opportunity to NetSuite sales order without an ecommerce frontend), see the Order-to-Cash Integration card instead. [src3]

| System | Role | API Surface | Direction |
|---|---|---|---|
| **Shopify** | Ecommerce storefront — order capture | REST Admin, GraphQL Admin, Webhooks | Outbound (orders) / Inbound (inventory) |
| **Adobe Commerce (Magento 2)** | Ecommerce storefront — order capture | REST, GraphQL, Message Queues | Outbound (orders) / Inbound (inventory) |
| **BigCommerce** | Ecommerce storefront — order capture | REST V2/V3, Webhooks | Outbound (orders) / Inbound (inventory) |
| **Oracle NetSuite** | ERP — financial master, fulfillment | SuiteTalk SOAP, REST API, RESTlets | Inbound (orders) / Outbound (inventory, fulfillment) |
| **SAP S/4HANA** | ERP — financial master, fulfillment | OData v4, IDoc | Inbound (orders) / Outbound (inventory, fulfillment) |
| **D365 Business Central** | ERP — financial master, fulfillment | OData v4, REST API v2.0 | Inbound (orders) / Outbound (inventory, fulfillment) |
| **iPaaS Middleware** | Orchestrator — data transformation | Celigo, Boomi, MuleSoft, Workato | Bidirectional |

## API Surfaces & Capabilities

| API Surface | Platform | Protocol | Best For | Max Records/Request | Rate Limit | Real-time? | Bulk? |
|---|---|---|---|---|---|---|---|
| GraphQL Admin API | Shopify | HTTPS/JSON | Orders, products, inventory (recommended) | 250 items per input | 100-1,000 pts/s by plan | Yes | Via Bulk Operations |
| REST Admin API | Shopify | HTTPS/JSON | Legacy, simple CRUD | 250 per page | 2 req/s (40 bucket) | Yes | No |
| Webhooks | Shopify | HTTPS/JSON | Event-driven order capture | 1 event per call | N/A (push) | Yes | No |
| REST API | Magento 2 | HTTPS/JSON | Orders, products, inventory | 300 per page (configurable) | Disabled by default | Yes | Via async endpoints |
| Async Bulk API | Magento 2 | HTTPS/JSON | High-volume inventory updates | 5,000 per request | Configurable via Redis | No | Yes |
| REST V3 API | BigCommerce | HTTPS/JSON | Orders, products, catalog | 250 per page | 150-450 req/30s by plan | Yes | No |
| Webhooks | BigCommerce | HTTPS/JSON | Event-driven order capture | 1 event per call | N/A (push) | Yes | No |
| SuiteTalk SOAP | NetSuite | HTTPS/XML | Order creation, inventory reads | 1,000 per search | 5 concurrent (default) | Yes | Via CSV import |
| REST API | NetSuite | HTTPS/JSON | Modern integration endpoint | 1,000 per page | Shared with SOAP | Yes | No |
| OData v4 | SAP S/4HANA | HTTPS/JSON | Sales orders, inventory | 5,000 per page ($top) | Throttled/fair-use | Yes | Via batch ($batch) |
| REST API v2.0 | D365 BC | HTTPS/JSON | Sales orders, items, inventory | 20,000 per page | Throttled/fair-use | Yes | Via $batch |

[src1, src4, src5]

## Rate Limits & Quotas

### Per-Request Limits

| Limit Type | Value | Platform | Notes |
|---|---|---|---|
| Max items per GraphQL mutation | 250 | Shopify | Use `bulkOperationRunMutation` for larger batches |
| Max query cost per request | 1,000 points | Shopify GraphQL | Single query cannot exceed this regardless of plan |
| Max REST response page size | 250 records | Shopify REST | Use `page_info` cursor pagination |
| Max records per search | 1,000 | NetSuite SuiteTalk | Use `searchMoreWithId` for pagination |
| Max $top per OData request | 5,000 | SAP S/4HANA | Server-driven paging for larger sets |
| Max request body | 100 MB | Magento 2 Async | For bulk async operations |
| Max webhook response time | 19 seconds | Shopify | Exceed = timeout + retry queue |

[src1, src4, src5]

### Rolling / Daily Limits

| Limit Type | Value | Window | Platform Differences |
|---|---|---|---|
| Shopify GraphQL points | 100-1,000/s | Per second (leaky bucket) | Standard: 100, Advanced: 200, Plus: 1,000, Enterprise: 2,000 |
| Shopify REST requests | 40 bucket (2/s refill) | Rolling | Shared across all apps on store |
| BigCommerce API requests | 150-450 / 30s | 30-second window | Standard/Plus: 150, Pro: 450, Enterprise: custom |
| BigCommerce webhooks | 60 / min | Per minute | Across all webhook consumers |
| NetSuite concurrent requests | 5-10 | Per account | Default 5; SuiteCloud Plus: 10+ |
| NetSuite governance units | 1,000 (scheduled) / 10,000 (map-reduce) | Per script execution | SuiteScript 2.x governance |

[src1, src4]

## Authentication

| Platform | Flow | Use When | Token Lifetime | Refresh? | Notes |
|---|---|---|---|---|---|
| Shopify | OAuth 2.0 | Public/custom apps | Access token: permanent (offline) | No refresh needed | Offline access tokens don't expire |
| Shopify | Custom App Token | Private integrations | Permanent | No | Generated in Shopify Admin; simplest for single-store |
| Magento 2 | OAuth 1.0a | Third-party integrations | Permanent (until revoked) | No | Requires consumer key/secret + access token/secret |
| Magento 2 | Bearer Token | Admin/customer API | 4 hours (admin), 1 hour (customer) | No | POST `/rest/V1/integration/admin/token` |
| BigCommerce | OAuth + API Token | All integrations | Permanent | No | Store-level API account; scoped permissions |
| NetSuite | Token-Based Auth (TBA) | Server-to-server | Permanent (until revoked) | No | Consumer key/secret + token key/secret; recommended over credentials |
| SAP S/4HANA | OAuth 2.0 Client Credentials | Server-to-server | Configurable (default 12h) | Yes | Requires x-csrf-token for write operations |
| D365 BC | OAuth 2.0 (Azure AD) | Server-to-server | 1 hour | Yes | Azure App Registration required; client_credentials flow |

[src1, src2, src3, src5]

### Authentication Gotchas

- **Shopify offline tokens never expire but can be revoked** — if a merchant uninstalls and reinstalls your app, you get a new token. Store tokens per-shop with install timestamp. [src1]
- **Magento 2 OAuth 1.0a is NOT OAuth 2.0** — you need a 4-step handshake (request token, authorize, access token, API call). Most HTTP libraries need an explicit OAuth 1.0a plugin. [src5]
- **NetSuite TBA requires enabling the feature** — go to Setup > Company > Enable Features > SuiteCloud > Token-Based Authentication. The integration record must also have TBA enabled separately. [src3]
- **SAP S/4HANA write operations require CSRF tokens** — fetch a token via `GET` with `x-csrf-token: fetch` header before every `POST`/`PATCH`/`DELETE`. Token is session-scoped. [src6]

## Constraints
<!-- Agents: read this section before recommending any integration approach from this unit.
     These are hard constraints that must not be violated. -->

- **Inventory sync must be unidirectional (ERP → ecommerce)** — the ERP is the inventory source of truth. Writing inventory from ecommerce to ERP creates ghost stock and overselling during concurrent warehouse operations. [src3, src6]
- **Shopify's leaky bucket rate limit is per-app, not per-endpoint** — all your order reads, inventory writes, and product updates share one bucket. A bulk product catalog sync can starve your order-fetching integration. [src1]
- **BigCommerce webhook callbacks must respond within 3 seconds** — acknowledge immediately with 200, then process asynchronously. Slow consumers get automatically unsubscribed after repeated timeouts. [src4]
- **NetSuite concurrent request limit (default 5) is per-account, not per-integration** — if you have multiple integrations (ecommerce + CRM + 3PL), they all share the same pool. Queue requests or upgrade to SuiteCloud Plus. [src3]
- **Magento 2 async bulk API requires RabbitMQ or MySQL message queue** — if your hosting doesn't support message queues, async endpoints return 400. Verify queue backend before designing bulk patterns. [src5]
- **Order IDs are NOT globally unique across platforms** — Shopify order #1001 and BigCommerce order #1001 are different orders. Always use platform-prefixed composite keys (e.g., `SHOP-1001`, `BC-1001`) as external IDs in ERP. [src3, src7]

## Integration Pattern Decision Tree

```
START — Ecommerce-to-ERP integration
├── What data flow?
│   ├── Orders (ecommerce → ERP)
│   │   ├── Volume < 100 orders/day?
│   │   │   ├── YES → Webhook-driven (near-real-time, simplest)
│   │   │   └── NO ↓
│   │   ├── Volume 100-1,000 orders/day?
│   │   │   ├── YES → Webhook + queue (RabbitMQ/SQS) → ERP batch insert
│   │   │   └── NO ↓
│   │   ├── Volume > 1,000 orders/day?
│   │   │   ├── YES → Webhook → queue → iPaaS batch processing (every 5-15min)
│   │   │   └── Flash sale / > 10K orders/day?
│   │   │       └── YES → iPaaS with auto-scaling + ERP bulk import API
│   │   └── Need immediate ERP confirmation?
│   │       ├── YES → Synchronous API call (watch rate limits!)
│   │       └── NO → Async webhook → queue → batch (recommended)
│   ├── Inventory (ERP → ecommerce)
│   │   ├── SKU count < 1,000?
│   │   │   ├── YES → Scheduled poll every 5-15min via REST
│   │   │   └── NO ↓
│   │   ├── SKU count < 50,000?
│   │   │   ├── YES → Delta sync (changed-since timestamp) every 5-15min
│   │   │   └── NO → Bulk export from ERP + delta sync for changes
│   │   └── Need < 1min latency?
│   │       ├── YES → ERP-side event (CDC/webhook) → push to ecommerce
│   │       └── NO → Scheduled delta sync (5-15min) — sufficient for most retailers
│   ├── Fulfillment/Tracking (ERP → ecommerce)
│   │   └── Always push-based: ERP triggers update → middleware → ecommerce fulfillment API
│   └── Product/Catalog (ERP → ecommerce)
│       └── Scheduled batch sync (hourly/daily) — product data is not time-critical
├── Which middleware?
│   ├── < 100 orders/day + single store → Direct API (no middleware needed)
│   ├── 100-1,000 orders/day → Celigo, Workato, or native connector
│   ├── > 1,000 orders/day → MuleSoft, Boomi, or Celigo with scaling tier
│   └── Multi-channel (Shopify + Magento + BigCommerce) → iPaaS required
└── Error handling?
    ├── Zero-loss required → Idempotent writes + dead letter queue + reconciliation job
    └── Best-effort acceptable → Retry with exponential backoff (max 5 retries)
```

## Quick Reference

| Step | Source System | Action | Target System | Data Objects | Failure Handling |
|---|---|---|---|---|---|
| 1 | Shopify/Magento/BC | Order placed → webhook fires | iPaaS Queue | Order header, line items, customer, shipping address | Webhook retries (Shopify: 19x/48h) |
| 2 | iPaaS | Transform + validate order data | ERP (NetSuite/SAP/D365) | Sales Order / Sales Invoice | Retry 3x with backoff, then DLQ |
| 3 | iPaaS | Create/match customer record | ERP | Customer / Business Partner | Match on email; create if not found |
| 4 | iPaaS | Map SKUs to ERP item IDs | ERP | Item / Material / Product | Fail order if SKU not found (alert) |
| 5 | ERP | Allocate inventory, create pick/pack | WMS / 3PL | Fulfillment record | Manual review queue |
| 6 | ERP | Ship order, generate tracking | iPaaS | Shipment + tracking number | Retry push to ecommerce |
| 7 | iPaaS | Update order with tracking | Shopify/Magento/BC | Fulfillment / Shipment record | Retry 3x, then alert |
| 8 | ERP | Adjust inventory quantities | iPaaS | Inventory levels per location | Delta sync on schedule |
| 9 | iPaaS | Push inventory updates | Shopify/Magento/BC | Inventory quantities per SKU | Retry, then full reconciliation |
| 10 | ERP | Invoice created, payment matched | Accounting | Invoice + payment | Manual reconciliation if mismatch |

[src2, src3, src6]

## Step-by-Step Integration Guide

### 1. Configure Webhook Listeners for Order Capture

Register webhooks on your ecommerce platform to receive order events. This eliminates polling and ensures near-real-time order flow. [src1, src4]

**Shopify** (GraphQL Admin API — recommended):
```graphql
mutation {
  webhookSubscriptionCreate(
    topic: ORDERS_CREATE
    webhookSubscription: {
      callbackUrl: "https://your-middleware.com/webhooks/shopify/orders"
      format: JSON
    }
  ) {
    webhookSubscription { id }
    userErrors { field message }
  }
}
```

**BigCommerce** (REST V3):
```bash
curl -X POST https://api.bigcommerce.com/stores/{store_hash}/v3/hooks \
  -H "X-Auth-Token: {api_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "store/order/created",
    "destination": "https://your-middleware.com/webhooks/bigcommerce/orders",
    "is_active": true
  }'
```

**Verify**: Check webhook registration — Shopify: Admin > Settings > Notifications > Webhooks; BigCommerce: `GET /v3/hooks`

### 2. Receive and Queue Order Webhooks

Never process orders synchronously in the webhook handler. Acknowledge immediately, then queue for async processing. [src3, src7]

```python
# Python (Flask) — webhook receiver with queue
import hmac, hashlib, json, base64
from flask import Flask, request, jsonify
import boto3  # or use RabbitMQ, Redis Queue, etc.

app = Flask(__name__)
sqs = boto3.client('sqs')
QUEUE_URL = "https://sqs.us-east-1.amazonaws.com/123456789/ecommerce-orders"

@app.route('/webhooks/shopify/orders', methods=['POST'])
def shopify_order_webhook():
    # Verify HMAC signature (critical for security)
    hmac_header = request.headers.get('X-Shopify-Hmac-Sha256', '')
    digest = hmac.new(
        b'YOUR_WEBHOOK_SECRET',
        request.data,
        hashlib.sha256
    ).digest()
    computed = base64.b64encode(digest).decode()
    if not hmac.compare_digest(computed, hmac_header):
        return jsonify({"error": "Invalid signature"}), 401

    # Queue immediately, respond fast (< 5s)
    order = json.loads(request.data)
    sqs.send_message(
        QueueUrl=QUEUE_URL,
        MessageBody=json.dumps({
            "platform": "shopify",
            "order_id": order["id"],
            "order_number": order["order_number"],
            "payload": order
        }),
        MessageGroupId="orders"
    )
    return jsonify({"status": "queued"}), 200
```

**Verify**: Send a test order → check queue depth increases by 1

### 3. Transform and Map Order Data to ERP Schema

Map ecommerce order fields to ERP-specific sales order format. This is where most integrations break. [src3, src7]

```python
# Python — Shopify order to NetSuite Sales Order mapping
def map_shopify_to_netsuite(shopify_order):
    """Transform Shopify order to NetSuite Sales Order JSON."""
    # Map customer
    customer = {
        "email": shopify_order["email"],
        "firstName": shopify_order["customer"]["first_name"],
        "lastName": shopify_order["customer"]["last_name"],
        "externalId": f"SHOP-{shopify_order['customer']['id']}"
    }

    # Map line items — SKU matching is critical
    items = []
    for item in shopify_order["line_items"]:
        items.append({
            "item": {"externalId": item["sku"]},  # Must match NetSuite item externalId
            "quantity": item["quantity"],
            "rate": float(item["price"]),
            "amount": float(item["price"]) * item["quantity"],
            "taxCode": {"externalId": "SHOPIFY-TAX"}  # Map tax separately
        })

    # Build Sales Order
    sales_order = {
        "externalId": f"SHOP-{shopify_order['order_number']}",
        "entity": {"externalId": f"SHOP-{shopify_order['customer']['id']}"},
        "tranDate": shopify_order["created_at"][:10],
        "status": "Pending Fulfillment",
        "itemList": {"item": items},
        "shippingAddress": {
            "addr1": shopify_order["shipping_address"]["address1"],
            "city": shopify_order["shipping_address"]["city"],
            "state": shopify_order["shipping_address"]["province_code"],
            "zip": shopify_order["shipping_address"]["zip"],
            "country": shopify_order["shipping_address"]["country_code"]
        }
    }
    return customer, sales_order
```

**Verify**: Run mapping on a sample order → validate all required ERP fields are populated, no nulls in mandatory fields

### 4. Create Sales Order in ERP with Idempotency

Use external IDs to make order creation idempotent — retrying the same order must not create duplicates. [src3]

```python
# Python — NetSuite SuiteTalk SOAP (using netsuite-sdk or zeep)
import requests
import json

NETSUITE_ACCOUNT = "1234567"
NETSUITE_URL = f"https://{NETSUITE_ACCOUNT}.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder"

def create_netsuite_order(sales_order, auth_headers):
    """Create sales order with idempotent externalId."""
    response = requests.post(
        NETSUITE_URL,
        json=sales_order,
        headers={
            **auth_headers,
            "Content-Type": "application/json",
            "Prefer": "return=representation"  # Return created record
        }
    )

    if response.status_code == 204:
        return {"status": "created", "id": response.headers.get("Location")}
    elif response.status_code == 409:
        # Duplicate externalId — order already exists (idempotent)
        return {"status": "already_exists"}
    elif response.status_code == 429:
        # Rate limited — back off and retry
        retry_after = int(response.headers.get("Retry-After", 5))
        raise RateLimitError(f"Retry after {retry_after}s")
    else:
        raise IntegrationError(f"NetSuite error {response.status_code}: {response.text}")
```

**Verify**: `GET /services/rest/record/v1/salesOrder?q=externalId IS "SHOP-1001"` → returns the created order

### 5. Sync Inventory from ERP to Ecommerce (Delta Sync)

Pull inventory changes from ERP since last sync, then push updates to ecommerce platform. Always ERP → ecommerce direction. [src2, src3]

```python
# Python — NetSuite → Shopify inventory sync (delta)
import requests
from datetime import datetime, timedelta

def sync_inventory_netsuite_to_shopify(last_sync_time, shopify_session):
    """Pull changed inventory from NetSuite, push to Shopify."""
    # 1. Query NetSuite for items changed since last sync
    suiteql = f"""
        SELECT item.itemId AS sku,
               il.quantityAvailable AS qty,
               il.location AS location_id
        FROM inventoryBalance il
        JOIN item ON il.item = item.id
        WHERE item.lastModifiedDate > '{last_sync_time}'
    """
    ns_response = requests.post(
        f"https://{ACCOUNT}.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql",
        json={"q": suiteql},
        headers=auth_headers
    )
    changed_items = ns_response.json().get("items", [])

    # 2. Push to Shopify via GraphQL inventorySetQuantities
    for item in changed_items:
        mutation = """
        mutation inventorySetQuantities($input: InventorySetQuantitiesInput!) {
          inventorySetQuantities(input: $input) {
            inventoryAdjustmentGroup { id }
            userErrors { field message }
          }
        }
        """
        variables = {
            "input": {
                "name": "available",
                "reason": "correction",
                "quantities": [{
                    "inventoryItemId": f"gid://shopify/InventoryItem/{item['inventory_item_id']}",
                    "locationId": f"gid://shopify/Location/{SHOPIFY_LOCATION_ID}",
                    "quantity": int(item["qty"])
                }]
            }
        }
        shopify_response = requests.post(
            f"https://{SHOP}.myshopify.com/admin/api/2025-01/graphql.json",
            json={"query": mutation, "variables": variables},
            headers={"X-Shopify-Access-Token": ACCESS_TOKEN}
        )
```

**Verify**: Change a quantity in NetSuite → run sync → check Shopify Admin > Products > Inventory matches

### 6. Push Fulfillment and Tracking Back to Ecommerce

When the ERP ships an order, push tracking information back to the ecommerce platform to trigger customer notifications. [src3]

```javascript
// Node.js — Push NetSuite fulfillment to Shopify
const axios = require('axios');

async function pushFulfillmentToShopify(orderId, trackingNumber, carrier) {
  const shopifyOrderId = orderId.replace('SHOP-', '');

  // 1. Get fulfillment order ID
  const foResponse = await axios.get(
    `https://${SHOP}.myshopify.com/admin/api/2025-01/orders/${shopifyOrderId}/fulfillment_orders.json`,
    { headers: { 'X-Shopify-Access-Token': ACCESS_TOKEN } }
  );
  const fulfillmentOrderId = foResponse.data.fulfillment_orders[0].id;

  // 2. Create fulfillment with tracking
  const fulfillment = await axios.post(
    `https://${SHOP}.myshopify.com/admin/api/2025-01/fulfillments.json`,
    {
      fulfillment: {
        line_items_by_fulfillment_order: [{
          fulfillment_order_id: fulfillmentOrderId
        }],
        tracking_info: {
          number: trackingNumber,
          company: carrier,  // 'ups', 'fedex', 'usps', 'dhl'
          url: `https://track.aftership.com/${trackingNumber}`
        },
        notify_customer: true
      }
    },
    { headers: { 'X-Shopify-Access-Token': ACCESS_TOKEN } }
  );

  return fulfillment.data;
}
```

**Verify**: `GET /admin/api/2025-01/orders/{id}/fulfillments.json` → status: `success`, tracking_number populated

## Code Examples

### Python: Shopify Bulk Inventory Update via GraphQL Bulk Operations

```python
# Input:  CSV of SKU → quantity mappings from ERP export
# Output: Shopify inventory levels updated for all changed SKUs

import requests
import json
import time

SHOP = "your-store.myshopify.com"
TOKEN = "shpat_xxxxx"
HEADERS = {
    "X-Shopify-Access-Token": TOKEN,
    "Content-Type": "application/json"
}
API_URL = f"https://{SHOP}/admin/api/2025-01/graphql.json"

def bulk_inventory_update(sku_qty_map, location_id):
    """Update inventory for many SKUs using Shopify bulk operations."""
    # Build JSONL for bulk mutation
    jsonl_lines = []
    for sku, qty in sku_qty_map.items():
        jsonl_lines.append(json.dumps({
            "input": {
                "name": "available",
                "reason": "correction",
                "quantities": [{
                    "inventoryItemId": sku_to_inventory_id(sku),
                    "locationId": f"gid://shopify/Location/{location_id}",
                    "quantity": qty
                }]
            }
        }))

    # Stage upload
    stage_mutation = """
    mutation { stagedUploadsCreate(input: [{
      resource: BULK_MUTATION_VARIABLES,
      filename: "inventory.jsonl",
      mimeType: "text/jsonl",
      httpMethod: POST
    }]) { stagedTargets { url parameters { name value } } } }
    """
    stage_resp = requests.post(API_URL, json={"query": stage_mutation}, headers=HEADERS)
    target = stage_resp.json()["data"]["stagedUploadsCreate"]["stagedTargets"][0]

    # Upload JSONL
    upload_params = {p["name"]: p["value"] for p in target["parameters"]}
    requests.post(target["url"], data=upload_params, files={"file": "\n".join(jsonl_lines)})

    # Run bulk mutation
    bulk_mutation = """
    mutation bulkInventoryUpdate($stagedUploadPath: String!) {
      bulkOperationRunMutation(
        mutation: "mutation($input: InventorySetQuantitiesInput!) { inventorySetQuantities(input: $input) { userErrors { message } } }",
        stagedUploadPath: $stagedUploadPath
      ) { bulkOperation { id status } }
    }
    """
    bulk_resp = requests.post(API_URL, json={
        "query": bulk_mutation,
        "variables": {"stagedUploadPath": upload_params.get("key", "")}
    }, headers=HEADERS)

    return bulk_resp.json()
```

### JavaScript/Node.js: BigCommerce Order Fetch with Rate Limit Handling

```javascript
// Input:  BigCommerce store credentials + date range
// Output: Array of order objects ready for ERP transformation

const axios = require('axios');

const BC_API = `https://api.bigcommerce.com/stores/${STORE_HASH}/v3`;
const HEADERS = {
  'X-Auth-Token': API_TOKEN,
  'Accept': 'application/json'
};

async function fetchOrdersWithRateLimit(sinceDate, maxRetries = 5) {
  let allOrders = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    try {
      const response = await axios.get(`${BC_API}/orders`, {
        headers: HEADERS,
        params: {
          min_date_created: sinceDate,
          sort: 'date_created:asc',
          limit: 250,
          page: page
        }
      });

      const orders = response.data.data;
      allOrders = allOrders.concat(orders);

      // Check rate limit headers
      const remaining = parseInt(response.headers['x-rate-limit-requests-left'] || '100');
      if (remaining < 10) {
        const resetMs = parseInt(response.headers['x-rate-limit-time-reset-ms'] || '5000');
        console.log(`Rate limit low (${remaining} left). Waiting ${resetMs}ms...`);
        await new Promise(r => setTimeout(r, resetMs));
      }

      hasMore = orders.length === 250;
      page++;
    } catch (err) {
      if (err.response?.status === 429) {
        const resetMs = parseInt(err.response.headers['x-rate-limit-time-reset-ms'] || '15000');
        console.log(`429 Rate limited. Retrying in ${resetMs}ms...`);
        await new Promise(r => setTimeout(r, resetMs));
        // Don't increment page — retry same page
      } else {
        throw err;
      }
    }
  }
  return allOrders;
}
```

### cURL: Quick API Tests for Each Platform

```bash
# --- Shopify: Test authentication + fetch recent orders ---
curl -s "https://YOUR-STORE.myshopify.com/admin/api/2025-01/orders.json?limit=5&status=any" \
  -H "X-Shopify-Access-Token: shpat_XXXXX" | jq '.orders | length'
# Expected: 5 (or fewer if store has < 5 orders)

# --- BigCommerce: Test auth + fetch orders ---
curl -s "https://api.bigcommerce.com/stores/STORE_HASH/v3/orders?limit=5" \
  -H "X-Auth-Token: YOUR_TOKEN" \
  -H "Accept: application/json" | jq '.data | length'
# Expected: 5

# --- Magento 2: Get bearer token + fetch orders ---
TOKEN=$(curl -s -X POST "https://your-magento.com/rest/V1/integration/admin/token" \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin123"}' | tr -d '"')

curl -s "https://your-magento.com/rest/V1/orders?searchCriteria[pageSize]=5" \
  -H "Authorization: Bearer $TOKEN" | jq '.items | length'
# Expected: 5

# --- NetSuite: Test SuiteQL query (TBA auth) ---
curl -s "https://ACCOUNT_ID.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: OAuth realm=ACCOUNT_ID, ..." \
  -d '{"q": "SELECT id, tranId FROM transaction WHERE type = '\''SalesOrd'\'' ORDER BY id DESC FETCH FIRST 5 ROWS ONLY"}' \
  | jq '.items | length'
# Expected: 5
```

## Data Mapping

### Field Mapping Reference

| Source Field (Ecommerce) | NetSuite Target | SAP S/4HANA Target | D365 BC Target | Type | Gotcha |
|---|---|---|---|---|---|
| order.id | salesOrder.externalId | SalesOrder.PurchaseOrderByCustomer | salesOrder.externalDocumentNumber | String | Prefix with platform code (SHOP-, BC-, MAG-) |
| order.order_number | salesOrder.otherRefNum | SalesOrder.CustomerPurchaseOrderNumber | salesOrder.number | String | Shopify order_number != id; use order_number for display |
| order.created_at | salesOrder.tranDate | SalesOrder.SalesOrderDate | salesOrder.orderDate | Date | Convert timezone: Shopify=UTC, NetSuite=user pref |
| order.total_price | salesOrder.total | SalesOrder.TotalNetAmount | salesOrder.totalAmountIncludingTax | Decimal | Currency alignment — verify 2 vs 4 decimal places |
| order.currency | salesOrder.currency.externalId | SalesOrder.TransactionCurrency | salesOrder.currencyCode | String | NetSuite uses internal IDs, not ISO codes by default |
| line_items[].sku | item.externalId | SalesOrderItem.Material | salesLine.itemNo | String | THE #1 FAILURE POINT — must match exactly |
| line_items[].quantity | item.quantity | SalesOrderItem.RequestedQuantity | salesLine.quantity | Integer | Check UOM mapping (each vs case vs pallet) |
| line_items[].price | item.rate | SalesOrderItem.NetAmount | salesLine.unitPrice | Decimal | Tax-inclusive (Shopify) vs tax-exclusive (most ERPs) |
| customer.email | customer.email | BusinessPartner.EmailAddress | customer.email | String | Primary match key for customer deduplication |
| shipping_address.zip | shippingAddress.zip | SalesOrder.ShipToParty.PostalCode | shipToAddress.postalCode | String | International zip formats vary (UK: SW1A 1AA) |

[src3, src7]

### Data Type Gotchas

- **Tax-inclusive vs tax-exclusive pricing**: Shopify stores prices tax-inclusive by default in EU/UK/AU stores but tax-exclusive in US. NetSuite and SAP typically store tax-exclusive amounts. You must decompose tax before inserting line items or totals will be wrong. [src3]
- **Currency decimal precision**: Shopify uses 2 decimal places for most currencies but some (JPY, KRW) use 0. SAP stores in smallest currency unit (cents). NetSuite uses the currency's natural precision. Always normalize to the ERP's expected format. [src7]
- **Date/timezone handling**: Shopify timestamps are UTC (ISO 8601). NetSuite dates depend on user timezone preference. SAP S/4HANA OData returns UTC. D365 BC returns UTC. Always convert to the ERP's expected timezone before creating records. [src3]
- **Shopify variant IDs vs SKUs**: A Shopify product has variants with both variant_id (numeric, Shopify-internal) and sku (string, merchant-defined). Always map on SKU, never on variant_id. Variant IDs change if the product is recreated. [src1]
- **BigCommerce product option IDs are store-specific**: Option IDs (like size=142, color=143) are auto-generated integers unique to each store. Map on option display_name + value, not on IDs. [src4]

## Error Handling & Failure Points

### Common Error Codes

| Code | Platform | Meaning | Cause | Resolution |
|---|---|---|---|---|
| 429 | All | Rate limit exceeded | Too many API calls in window | Exponential backoff: wait `2^n * 1s`, max 5 retries. Read Retry-After / X-Rate-Limit-Time-Reset-Ms header |
| 422 | Shopify | Unprocessable Entity | Invalid data (missing required field, bad format) | Check response body for field-level errors; fix data mapping |
| 401 | All | Unauthorized | Expired or invalid token | Re-authenticate; for Shopify check app isn't uninstalled |
| 409 | NetSuite | Conflict / Duplicate | externalId already exists | Idempotent — this is success for retry scenarios |
| 400 | Magento | Bad Request | Invalid search criteria or missing queue backend | Verify searchCriteria syntax; check RabbitMQ running for async |
| RCRD_DSNT_EXIST | NetSuite | Record not found | Referenced item/customer doesn't exist | Verify SKU mapping; create missing master data first |
| INSUFFICIENT_PERMISSIONS | NetSuite | Permission denied | Integration role missing record-type permission | Audit integration role; add required permissions |

[src1, src3, src4, src5]

### Failure Points in Production

- **SKU not found in ERP**: Orders with unmapped SKUs silently fail or create orders with missing line items. Fix: `Implement a SKU validation step before order creation — query ERP for all SKUs in the order, reject with alert if any missing`. [src3]
- **Webhook deduplication failure**: Shopify can send the same webhook event multiple times. Fix: `Store processed webhook IDs (X-Shopify-Webhook-Id header) in Redis/DB with 48h TTL; skip if already processed`. [src1]
- **OAuth token revocation during peak traffic**: Merchant reinstalls app or changes permissions mid-sync. Fix: `Catch 401 errors, trigger re-auth flow, queue failed orders for retry after re-authentication`. [src2]
- **Partial fulfillment mismatch**: ERP ships partial order but ecommerce platform expects full fulfillment object. Fix: `Map ERP partial shipments to ecommerce partial fulfillments; track fulfillment state per line item, not per order`. [src3]
- **Inventory count drift over time**: Small discrepancies accumulate between ERP and ecommerce, even with delta sync. Fix: `Run a full inventory reconciliation job weekly — compare all SKU quantities between systems and correct mismatches`. [src6, src7]
- **Flash sale traffic kills integration**: Thousands of webhooks arrive simultaneously, overwhelming middleware. Fix: `Use a message queue (SQS/RabbitMQ) as buffer between webhooks and ERP writes; auto-scale queue consumers; implement circuit breaker on ERP API calls`. [src3]

## Anti-Patterns

### Wrong: Polling ecommerce platform for new orders every 60 seconds

```python
# BAD — wastes API quota, adds latency, and hits rate limits at scale
while True:
    orders = shopify.get("/orders.json?status=any&created_at_min=" + last_check)
    for order in orders:
        create_erp_order(order)
    time.sleep(60)
```

### Correct: Use webhooks with queue-based processing

```python
# GOOD — event-driven, near-real-time, respects rate limits
@app.route('/webhooks/shopify/orders', methods=['POST'])
def handle_order_webhook():
    verify_hmac(request)  # Always verify!
    queue.enqueue(request.data)  # Queue immediately
    return '', 200  # Respond fast
# Queue consumer processes orders at sustainable rate
```

### Wrong: Bidirectional inventory sync without conflict resolution

```python
# BAD — creates infinite loops and ghost stock
def sync_inventory():
    shopify_qty = get_shopify_inventory(sku)
    netsuite_qty = get_netsuite_inventory(sku)
    if shopify_qty != netsuite_qty:
        update_netsuite(sku, shopify_qty)  # Wrong! Shopify is not the source of truth
        update_shopify(sku, netsuite_qty)  # This overwrites what you just pushed to NetSuite
```

### Correct: Unidirectional inventory sync (ERP as source of truth)

```python
# GOOD — ERP is single source of truth for inventory
def sync_inventory():
    changed_items = query_netsuite_changed_since(last_sync_time)
    for item in changed_items:
        update_shopify_inventory(
            sku=item["sku"],
            quantity=item["quantityAvailable"],
            location_id=SHOPIFY_LOCATION_ID
        )
    save_last_sync_time(datetime.utcnow())
```

### Wrong: Hardcoded SKU mapping in code

```python
# BAD — breaks every time a new product is added
SKU_MAP = {
    "BLK-SHIRT-L": "10042",
    "WHT-SHIRT-M": "10043",
    # 2,000 more hardcoded entries...
}
```

### Correct: Dynamic SKU lookup with ERP external IDs

```python
# GOOD — SKU matching via ERP external ID field
def get_erp_item_id(sku):
    result = netsuite_search("item", {"externalId": sku})
    if not result:
        raise SKUNotFoundError(f"SKU '{sku}' not found in ERP. "
                               "Add item to ERP or create mapping.")
    return result["internalId"]
```

[src3, src6, src7]

## Common Pitfalls

- **SKU format mismatch between platforms**: Shopify uses `BLK-SHIRT-L`, ERP uses `BLACK_SHIRT_LARGE`. Fix: `Create a SKU mapping table (ecommerce_sku → erp_item_id) maintained in the iPaaS or a shared database. Validate all SKUs exist before processing orders.` [src3, src7]
- **Ignoring Shopify's webhook HMAC verification**: Skipping signature verification exposes your integration to spoofed orders and potential fraud. Fix: `Always verify X-Shopify-Hmac-Sha256 header. BigCommerce doesn't sign webhooks — use IP allowlisting or a shared secret query parameter.` [src1]
- **Not handling Shopify's order editing after creation**: Merchants edit orders (add items, change quantities, apply discounts) after creation. Fix: `Subscribe to orders/updated webhook in addition to orders/create. Implement order amendment logic in ERP (modify existing SO, not create new).` [src2]
- **Testing with production API limits**: Sandbox/development stores have different rate limits. Shopify development stores have lower limits than Plus stores. Fix: `Load-test against a representative environment. Simulate peak traffic (Black Friday: 10-50x normal volume) before go-live.` [src1]
- **Assuming tax calculation matches between systems**: Shopify calculates tax at checkout; NetSuite recalculates based on its tax engine. Fix: `Decide one source of truth for tax. Either pass Shopify tax amounts as override, or let ERP recalculate and accept small rounding differences (< $0.01).` [src3]
- **Not implementing dead letter queues for failed orders**: Failed orders disappear silently without a DLQ. Fix: `After max retries, move failed orders to a DLQ table with the original payload + error details. Build an admin dashboard for manual review and re-submission.` [src7]
- **Single-threaded webhook processing**: Processing webhooks sequentially creates a bottleneck during peak traffic. Fix: `Use competing consumers pattern — multiple queue workers processing from the same queue. Scale workers based on queue depth.` [src6]

## Diagnostic Commands

```bash
# --- Shopify: Check API usage / remaining limits ---
curl -sI "https://YOUR-STORE.myshopify.com/admin/api/2025-01/shop.json" \
  -H "X-Shopify-Access-Token: TOKEN" | grep -i "x-shopify"
# Look for: X-Shopify-Shop-Api-Call-Limit: 2/40

# --- BigCommerce: Check rate limit headers ---
curl -sI "https://api.bigcommerce.com/stores/HASH/v3/catalog/products?limit=1" \
  -H "X-Auth-Token: TOKEN" | grep -i "x-rate-limit"
# Look for: X-Rate-Limit-Requests-Left, X-Rate-Limit-Time-Reset-Ms

# --- Shopify: Verify webhook registrations ---
curl -s "https://YOUR-STORE.myshopify.com/admin/api/2025-01/webhooks.json" \
  -H "X-Shopify-Access-Token: TOKEN" | jq '.webhooks[] | {id, topic, address}'

# --- NetSuite: Test SuiteQL connectivity ---
curl -s "https://ACCOUNT.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql" \
  -X POST -H "Content-Type: application/json" \
  -H "Authorization: OAuth ..." \
  -d '{"q": "SELECT COUNT(*) AS cnt FROM transaction WHERE type = '\''SalesOrd'\''"}' \
  | jq '.items[0].cnt'

# --- Magento: Check integration token validity ---
curl -s "https://your-magento.com/rest/V1/store/storeConfigs" \
  -H "Authorization: Bearer YOUR_TOKEN" | jq '.[0].code'
# Expected: store code string (not 401 error)

# --- Shopify: Verify inventory levels for a SKU ---
curl -s "https://YOUR-STORE.myshopify.com/admin/api/2025-01/inventory_levels.json?inventory_item_ids=ITEM_ID" \
  -H "X-Shopify-Access-Token: TOKEN" | jq '.inventory_levels[] | {location_id, available}'
```

## Version History & Compatibility

| Component | Current Version | Release Date | Status | Breaking Changes | Notes |
|---|---|---|---|---|---|
| Shopify Admin API | 2025-01 | 2025-01 | Current | REST deprecation push toward GraphQL | Use versioned endpoints; unversioned calls get latest |
| Shopify Admin API | 2024-10 | 2024-10 | Supported | N/A | Minimum 12 months support per version |
| Magento / Adobe Commerce | 2.4.7 | 2024-04 | Current | Async bulk API improvements | PHP 8.2+ required |
| BigCommerce REST API | V3 | 2020 (ongoing) | Current | V2 endpoints being deprecated | Always use V3 where available |
| NetSuite REST API | 2024.2 | 2024-08 | Current | N/A | SuiteTalk SOAP still supported but REST preferred |
| SAP S/4HANA OData | 2408 | 2024-08 | Current | Async OData processing | x-csrf-token required for writes |
| D365 Business Central | API v2.0 | 2023 (ongoing) | Current | N/A | OData v4 with Azure AD auth |

[src1, src4, src5]

### Deprecation Policy

Shopify supports each API version for at least 12 months after release; deprecated versions return `X-Shopify-API-Deprecated-Reason` headers before removal. [src1] BigCommerce V2 endpoints are being phased out in favor of V3 — check for V3 equivalents before building new integrations. [src4] Magento follows Adobe's lifecycle policy with quarterly patches and annual major releases. [src5]

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Automating order flow from Shopify/Magento/BigCommerce into any ERP | You only sell on one marketplace (Amazon/eBay) | Marketplace-specific integration patterns |
| Need real-time inventory visibility across online + warehouse | ERP doesn't have an API (legacy on-premise with no REST/SOAP) | File-based integration (CSV/SFTP drop) |
| Processing 100-10,000+ orders/day | < 10 orders/day and manual entry is acceptable | Manual order entry in ERP |
| Running multi-channel ecommerce (multiple storefronts) | Single Shopify store with Shopify's native inventory | Shopify's built-in inventory management |
| Need automated fulfillment + tracking number pushback | ERP is only for financials, not fulfillment | Simple accounting sync (journal entries only) |

## Cross-System Comparison

| Capability | Shopify | Adobe Commerce (Magento 2) | BigCommerce | Notes |
|---|---|---|---|---|
| **API Style** | REST + GraphQL | REST + GraphQL + SOAP | REST (V2/V3) + GraphQL (Storefront) | Shopify pushing GraphQL as primary |
| **Rate Limits** | 100-1,000 pts/s (GraphQL) | Disabled by default (configurable) | 150-450 req/30s by plan | Magento most permissive; Shopify most structured |
| **Webhook Support** | Excellent (HMAC signed) | Via observers + message queue | Good (no signature) | Shopify webhooks most reliable |
| **Bulk Operations** | GraphQL Bulk Operations | Async bulk REST endpoints | No native bulk API | Shopify best for large catalog updates |
| **Auth Model** | OAuth 2.0 / API key | OAuth 1.0a / bearer token | OAuth + API token | Magento's OAuth 1.0a is oldest pattern |
| **Inventory API** | inventorySetQuantities (GraphQL) | POST /V1/inventory/source-items | PUT /v3/inventory/items | Shopify: multi-location native; Magento: MSI module |
| **Order API** | Orders resource + Fulfillment Orders | Orders resource + Shipments | Orders V2/V3 + Shipments | Shopify fulfillment_orders newer, more flexible |
| **Multi-store** | Shopify Markets (single admin) | Multi-website architecture | Multi-storefront | Magento most flexible for B2B multi-store |
| **Native ERP Connectors** | Global ERP Program (NetSuite, D365 BC, Infor, Acumatica) | Adobe Commerce connectors | App marketplace | Shopify most mature ERP partner program |
| **Pagination** | Cursor-based (page_info) | Page-based (searchCriteria) | Page-based (page + limit) | Shopify cursor-based most scalable |

[src1, src2, src4, src5, src6]

## Important Caveats

- **Rate limits differ dramatically by plan tier** — a Shopify Basic store gets 100 GraphQL points/s while Plus gets 1,000/s. Design your integration for the lowest plan you'll support, or build adaptive throttling. [src1]
- **Webhook reliability is not 100%** — Shopify retries failed webhooks up to 19 times over 48 hours, then stops. BigCommerce auto-unsubscribes slow consumers. Always run a reconciliation job to catch missed events. [src1, src4]
- **iPaaS costs add up** — Celigo ~$16,500/year, Jitterbit ~$19,600/year, Boomi ~$300/user/month. For < 100 orders/day, direct API integration without middleware may be more cost-effective. [src3]
- **Shopify is deprecating REST in favor of GraphQL** — while REST Admin API still works, new features are GraphQL-only. Plan migration to GraphQL for long-term maintenance. [src1]
- **Test during simulated peak load** — 48% of retailers lose > $50,000/year due to poor integration, typically manifesting during peak traffic (Black Friday, flash sales) when the integration breaks under load. [src7]
- **This card covers API integration patterns, not specific iPaaS configuration** — for step-by-step Celigo/Boomi/MuleSoft configuration, consult the respective platform's connector documentation. [src6]

## Related Units

- [Batch vs Real-time Integration Patterns](/business/erp-integration/batch-vs-realtime-integration/2026)
- [Order-to-Cash Cross-System Integration](/business/erp-integration/order-to-cash-integration/2026)
- [NetSuite SuiteTalk API Capabilities](/business/erp-integration/netsuite-suitetalk-api-capabilities/2026)
- [Idempotency in ERP Integrations](/business/erp-integration/idempotency-erp-integrations/2026)
- [Error Handling and Dead Letter Queues](/business/erp-integration/error-handling-dead-letter-queues/2026)
- [Master Data Management (MDM) for ERP](/business/erp-integration/master-data-management-erp/2026)
