← All Posts
/ Blog

How to Integrate Your Inventory System with Shopify: A Manufacturer's Guide

2026-03-03  ·  shopify, inventory, integration, manufacturing, D2C, API, ecommerce

# How to Integrate Your Inventory System with Shopify: A Manufacturer's Guide

Selling direct-to-consumer (D2C) through Shopify while running manufacturing operations creates a classic inventory nightmare. Your shop floor system says you have 500 units. Shopify says 487. A customer just ordered 20, but you actually only have 12 because 8 were allocated to a wholesale order that hasn't shipped yet.

This guide walks through integrating your manufacturing inventory system with Shopify—the right way.

Why Integration Matters for Manufacturers

Manual inventory updates between systems create three problems:

  1. Overselling — You sell units that don't exist, damaging customer trust
  2. Underselling — You hide available inventory, leaving money on the table
  3. Operational drag — Someone spends hours every day updating stock levels instead of making things

Real-time inventory sync isn't just convenient—it's competitive advantage.

The Integration Architecture

Most manufacturer-to-Shopify integrations follow this pattern:

` ┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐ │ Manufacturing │◄───►│ Integration │◄───►│ Shopify │ │ System (ERP) │ │ Layer (Your API)│ │ Store │ └─────────────────┘ └──────────────────┘ └─────────────┘ │ │ ▼ ▼ Raw materials Orders flow Work orders Customer data Finished goods Shipping updates `

The integration layer handles:

Step-by-Step Implementation

Step 1: Map Your Data Fields

Before writing code, document how fields translate between systems:

| Manufacturing System | Shopify | Notes | |---------------------|---------|-------| | SKU-001-BLU | sku-001-blue | Shopify uses lowercase, hyphens | | Finished Goods Warehouse | Location ID: 123456789 | Multi-location manufacturing needs mapping | | Qty Available (minus allocations) | inventory_quantity | Don't sync gross inventory | | Lot L-2025-03-A | inventory_item.inventory_levels | Track lot numbers in Shopify metafields |

Key decision: Do you sync "available to promise" inventory or "physical on-hand"? For D2C, use available-to-promise—physical inventory minus allocations for existing orders, safety stock, and wholesale commitments.

Step 2: Set Up Authentication

Shopify uses OAuth 2.0 for app authentication. Your manufacturing system will vary.

Shopify authentication flow: `javascript // 1. Redirect merchant to Shopify to authorize const authUrl = https://${shop}.myshopify.com/admin/oauth/authorize? + client_id=${API_KEY}& + scope=read_products,write_products,read_inventory,write_inventory& + redirect_uri=${encodeURIComponent(redirectUri)}& + state=${nonce};

// 2. Exchange temporary code for permanent access token const tokenResponse = await fetch(https://${shop}.myshopify.com/admin/oauth/access_token, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_id: API_KEY, client_secret: API_SECRET, code: temporaryCode }) });

const { access_token } = await tokenResponse.json(); // Store access_token securely—this is your permanent API key `

Store credentials securely. Never hardcode tokens. Use environment variables or a secrets manager like AWS Secrets Manager or HashiCorp Vault.

Step 3: Build the Inventory Sync

The core loop: poll your manufacturing system for changes, push updates to Shopify.

`javascript // Manufacturing system → Shopify sync async function syncInventoryToShopify() { // 1. Get changed inventory from manufacturing system const lastSync = await getLastSyncTimestamp(); const inventoryChanges = await manufacturingAPI.getInventoryChanges(lastSync);

for (const item of inventoryChanges) { try { // 2. Map manufacturing SKU to Shopify variant ID const shopifyVariantId = await getShopifyVariantId(item.sku);

if (!shopifyVariantId) { console.warn(SKU ${item.sku} not found in Shopify); continue; }

// 3. Calculate available inventory const availableQty = item.onHand - item.allocated - item.safetyStock;

// 4. Update Shopify inventory level await fetch(https://${shop}.myshopify.com/admin/api/2024-01/inventory_levels/set.json, { method: 'POST', headers: { 'X-Shopify-Access-Token': accessToken, 'Content-Type': 'application/json' }, body: JSON.stringify({ location_id: shopifyLocationId, inventory_item_id: shopifyVariantId, available: Math.max(0, availableQty) }) });

// 5. Log successful sync await logSync(item.sku, availableQty, 'success');

} catch (error) { // 6. Handle errors—retry or alert await logSync(item.sku, null, 'error', error.message); await alertOperationsTeam(Sync failed for ${item.sku}: ${error.message}); } }

// 7. Update last sync timestamp await setLastSyncTimestamp(new Date().toISOString()); }

// Run every 5 minutes setInterval(syncInventoryToShopify, 5 60 1000); `

Step 4: Handle Orders Flowing Back

When Shopify sells something, you need that order in your manufacturing system for fulfillment.

Shopify webhook setup: `javascript // Create a webhook to notify your system of new orders await fetch(https://${shop}.myshopify.com/admin/api/2024-01/webhooks.json, { method: 'POST', headers: { 'X-Shopify-Access-Token': accessToken, 'Content-Type': 'application/json' }, body: JSON.stringify({ webhook: { topic: 'orders/create', address: 'https://your-integration-server.com/webhooks/shopify/orders', format: 'json' } }) }); `

Webhook handler: `javascript app.post('/webhooks/shopify/orders', async (req, res) => { // 1. Verify webhook authenticity (Shopify signs webhooks) const hmac = req.headers['x-shopify-hmac-sha256']; const hash = crypto .createHmac('sha256', webhookSecret) .update(req.body, 'utf8') .digest('base64');

if (hmac !== hash) { return res.status(401).send('Unauthorized'); }

// 2. Process the order const order = req.body;

await manufacturingAPI.createSalesOrder({ orderNumber: order.name, customer: { name: ${order.customer.first_name} ${order.customer.last_name}, email: order.email }, lineItems: order.line_items.map(item => ({ sku: item.sku, quantity: item.quantity, unitPrice: item.price })), shippingAddress: order.shipping_address, source: 'shopify' });

res.status(200).send('OK'); }); `

Step 5: Handle Multi-Location Manufacturing

If you manufacture in multiple locations, you need a location mapping strategy:

`javascript const locationMapping = { 'WAREHOUSE_A': { shopifyLocationId: '123456789', shopifyLocationName: 'Main Warehouse' }, 'FACTORY_B': { shopifyLocationId: '987654321', shopifyLocationName: 'West Coast Facility' } };

// Sync inventory for each location separately for (const [mfgLocation, shopifyLocation] of Object.entries(locationMapping)) { const inventory = await manufacturingAPI.getInventoryByLocation(mfgLocation);

for (const item of inventory) { await updateShopifyInventory( item.sku, shopifyLocation.shopifyLocationId, item.available ); } } `

Common Pitfalls (And How to Avoid Them)

1. Race Conditions

Problem: Two orders come in simultaneously. Both see 10 units available. Both sell 10 units. You're now -10.

Solution: Shopify handles inventory reservation at checkout, but you need atomic updates in your manufacturing system. Use database transactions or inventory reservation patterns:

`javascript // Reserve inventory before confirming order await manufacturingAPI.reserveInventory({ sku: orderItem.sku, quantity: orderItem.quantity, orderId: order.id, expiresAt: Date.now() + (30 60 1000) // 30 min reservation }); `

2. API Rate Limits

Shopify allows 40 requests/second for standard plans, 80 for Shopify Plus. Bursting through these limits breaks your integration.

Solution: Implement rate limiting and batching:

`javascript import Bottleneck from 'bottleneck';

const limiter = new Bottleneck({ minTime: 25, // 40 requests per second max maxConcurrent: 10 });

const updateShopifyInventory = limiter.wrap(async (sku, locationId, qty) => { // Your update logic here }); `

3. SKU Mismatches

Your manufacturing system calls it "BRACKET-V2-BLU." Shopify has "bracket-v2-blue." The sync silently fails.

Solution: Maintain a SKU mapping table and validate mappings regularly:

`javascript // Validate all manufacturing SKUs exist in Shopify const validationReport = await validateSkuMappings(); if (validationReport.missing.length > 0) { await alertOperationsTeam( Missing Shopify SKUs: ${validationReport.missing.join(', ')} ); } `

4. Ignoring Shopify's Inventory Levels

Shopify tracks inventory at the inventory_item level, not the product level. Updating the wrong level creates discrepancies.

Solution: Always update inventory_levels for the specific location, using the inventory_item_id from the variant:

`javascript // Get the correct inventory_item_id const variant = await shopifyAPI.get(/variants/${variantId}.json); const inventoryItemId = variant.inventory_item_id;

// Update the inventory level at the specific location await shopifyAPI.post('/inventory_levels/set.json', { location_id: locationId, inventory_item_id: inventoryItemId, available: newQuantity }); `

5. No Error Recovery

Your integration goes down. Shopify orders pile up. When it restarts, you have 50 unprocessed orders and angry customers.

Solution: Build idempotency and replay capability:

`javascript // Store processed order IDs to avoid duplicates const processedOrderIds = new Set(await getProcessedOrderIds());

app.post('/webhooks/shopify/orders', async (req, res) => { const orderId = req.body.id;

if (processedOrderIds.has(orderId)) { return res.status(200).send('Already processed'); }

// Process order... await manufacturingAPI.createSalesOrder(req.body); await markOrderProcessed(orderId);

res.status(200).send('OK'); }); `

Testing Your Integration

Before going live:

  1. Sandbox testing — Use Shopify's development stores and your manufacturing system's test environment
  2. Volume testing — Simulate Black Friday-level traffic (100+ orders/minute)
  3. Failure testing — Kill the integration mid-sync. Verify recovery.
  4. Edge cases — Test zero inventory, negative inventory, and discontinued SKUs

Tools and Platforms That Can Help

Don't want to build from scratch? Consider these integration platforms:

Measuring Success

Track these metrics:

Final Thoughts

Inventory integration isn't a one-time project—it's an ongoing operational system. Start simple (one-way sync of inventory levels), get it stable, then add bidirectional order flow. Monitor religiously. Fix errors fast. Your customers will thank you with repeat business.

Need help finding the right APIs? Browse the ForgeDirectory manufacturing API catalog for authentication details, endpoint references, and integration patterns for popular inventory and ERP systems.

Find the right manufacturing API

Browse our directory of vetted manufacturing APIs. Filter by category, integration type, and pricing.

Browse Directory