Skip to main content

Inventory Sync Guide

Use ECOSIRE as the single source of truth for inventory and push stock levels to all your sales channels in real time — preventing overselling and keeping every channel accurate.


Introduction

Managing inventory across multiple channels (Shopify, WooCommerce, Amazon, a physical store) without a central system leads to overselling, stock discrepancies, and customer complaints. ECOSIRE solves this by:

  1. Centralizing all stock movements (receipts, sales, returns, adjustments)
  2. Publishing updated stock levels to all connected channels after each movement
  3. Reserving stock when an order is confirmed (before it ships)

Prerequisites

  • ECOSIRE account with Inventory module enabled
  • API keys for your sales channels
  • A server or serverless function to run the sync (Node.js or Python)
  • ECOSIRE API key

Step 1 — Understand ECOSIRE's Inventory Model

Every product has three stock quantities:

FieldMeaning
onHandPhysical units in the warehouse
reservedUnits allocated to confirmed orders (not yet shipped)
availableonHand - reserved — what you can actually sell

Always use available as the stock quantity you publish to sales channels.


Step 2 — Fetch Current Stock

async function getAvailableStock(productIds?: string[]): Promise<StockRecord[]> {
const params = new URLSearchParams({ limit: '100' });
if (productIds?.length) params.set('productIds', productIds.join(','));

const res = await fetch(`https://api.ecosire.com/api/inventory/products?${params}`, {
headers: { Authorization: `Bearer ${process.env.ECOSIRE_API_KEY}` },
});
const { data } = await res.json();
return data.map((p: any) => ({
ecosireProductId: p.productId,
sku: p.sku,
available: p.available,
}));
}

Step 3 — Push Stock to Shopify

async function pushToShopify(stockRecords: StockRecord[]) {
const locationId = process.env.SHOPIFY_LOCATION_ID!;

for (const record of stockRecords) {
// Look up Shopify inventory item ID by SKU
const { inventory_item_id } = await getShopifyVariantBySku(record.sku);
if (!inventory_item_id) continue;

await fetch(`https://your-store.myshopify.com/admin/api/2024-01/inventory_levels/set.json`, {
method: 'POST',
headers: {
'X-Shopify-Access-Token': process.env.SHOPIFY_ACCESS_TOKEN!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
location_id: locationId,
inventory_item_id,
available: record.available,
}),
});
console.log(`Shopify: SKU ${record.sku}${record.available} units`);
}
}

Step 4 — Push Stock to WooCommerce

import WooCommerceRestApi from '@woocommerce/woocommerce-rest-api';

const wc = new WooCommerceRestApi({
url: process.env.WC_SITE_URL!,
consumerKey: process.env.WC_CONSUMER_KEY!,
consumerSecret: process.env.WC_CONSUMER_SECRET!,
version: 'wc/v3',
});

async function pushToWooCommerce(stockRecords: StockRecord[], skuToWcId: Record<string, number>) {
for (const record of stockRecords) {
const wcId = skuToWcId[record.sku];
if (!wcId) continue;

await wc.put(`products/${wcId}`, {
stock_quantity: record.available,
manage_stock: true,
});
console.log(`WooCommerce: SKU ${record.sku}${record.available} units`);
}
}

Step 5 — Set Up Real-Time Sync via Webhooks

Instead of polling every 15 minutes, use ECOSIRE webhooks to trigger immediate stock pushes on inventory events.

Register for inventory-related events (if available) or use the checkout.completed event as a trigger:

// When an ECOSIRE order is confirmed, stock is reserved
// Push updated "available" quantity to all channels immediately

app.post('/internal/on-order-confirmed', async (req, res) => {
const { data } = req.body;
const productIds = data.lineItems.map((li: any) => li.productId);

// Fetch updated stock for affected products only
const stock = await getAvailableStock(productIds);

// Push to all channels in parallel
await Promise.all([
pushToShopify(stock),
pushToWooCommerce(stock, skuToWcIdMap),
]);

res.json({ ok: true });
});

Step 6 — Handle Returns and Adjustments

When stock is returned or adjusted in ECOSIRE, trigger a re-sync:

async function onStockAdjustment(adjustedProductIds: string[]) {
const stock = await getAvailableStock(adjustedProductIds);
await Promise.all([
pushToShopify(stock),
pushToWooCommerce(stock, skuToWcIdMap),
]);
}

This function should be called after:

  • POST /api/inventory/adjustments completes
  • A transfer is validated
  • A sales return is processed

Step 7 — Schedule a Full Reconciliation

Run a full stock reconciliation once per day as a safety net:

// Run daily at 2am UTC via cron
async function fullReconciliation() {
const allStock = await getAvailableStock();
await Promise.all([
pushToShopify(allStock),
pushToWooCommerce(allStock, skuToWcIdMap),
]);
console.log(`[${new Date().toISOString()}] Full reconciliation: ${allStock.length} SKUs`);
}

Troubleshooting

IssueSolution
Overselling continuesConfirm stock reservation is enabled in ECOSIRE settings
Stock mismatch after syncRun a full reconciliation; check for manual edits in channel admin
Shopify API rate limitBatch requests; add 500ms delay between items
WooCommerce product not foundVerify SKU matches exactly (case-sensitive)

Next Steps