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:
- Centralizing all stock movements (receipts, sales, returns, adjustments)
- Publishing updated stock levels to all connected channels after each movement
- 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:
| Field | Meaning |
|---|---|
onHand | Physical units in the warehouse |
reserved | Units allocated to confirmed orders (not yet shipped) |
available | onHand - 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/adjustmentscompletes- 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
| Issue | Solution |
|---|---|
| Overselling continues | Confirm stock reservation is enabled in ECOSIRE settings |
| Stock mismatch after sync | Run a full reconciliation; check for manual edits in channel admin |
| Shopify API rate limit | Batch requests; add 500ms delay between items |
| WooCommerce product not found | Verify SKU matches exactly (case-sensitive) |
Next Steps
- Inventory API — Full inventory endpoint reference
- Shopify Integration — Complete Shopify setup guide
- WordPress Integration — WooCommerce sync guide