Sync Inventory Between Your Online Store and Accounting Software via API
Automating inventory synchronization between an e-commerce platform (such as Shopify or WooCommerce) and accounting software (such as QuickBooks Online or Xero) prevents overselling and eliminates manual data entry errors. This guide outlines the architecture, data mapping, and implementation code required for a robust, event-driven API integration.
Integration Architecture: Webhooks vs. Polling
To keep stock levels aligned in near-real time, use an event-driven architecture based on webhooks rather than scheduled polling:
- Webhooks (Event-Driven): Your store fires an HTTP POST payload to a custom middleware endpoint immediately when an order is placed, cancelled, or restocked. This minimizes API call volume and reduces sync delay.
- Polling (Fallback Reconciliation): A scheduled background cron job queries both APIs daily to catch missed webhooks, reconcile discrepancies, and fix inventory drift.
Step 1: Map Core Identifiers and Attributes
Both systems must share a single source of truth for stock keeping. Standardize the following attributes before writing code:
- SKU / Item Code: Must match exactly between the store product variant and the accounting item. Do not use platform-generated internal database IDs (e.g., Shopify
variant_idvs. QuickBooksItemRef). - Location Identifiers: Map store fulfillment location IDs to corresponding warehouse IDs or classes in your accounting software.
- Adjustment Reasons: Standardize mapping for quantity updates (e.g., Sales Order, Return, Shrinkage, Restock).
Step 2: Implement the Webhook Listener & API Gateway
The middle tier receives the webhook, extracts the SKU and new stock quantity, looks up the target item in the accounting API, and posts an inventory adjustment.
Below is an Express.js (Node.js) sample demonstrating a complete flow from store webhook to accounting API update:
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
// Endpoint receiving store inventory update webhooks
app.post('/api/webhooks/inventory-update', async (req, res) => {
const { sku, updated_quantity, location_id } = req.body;
if (!sku) {
return res.status(400).send('Missing SKU identifier');
}
try {
// 1. Fetch matching item from Accounting Software API
const searchResponse = await axios.get(`https://api.accounting.com/v1/items`, {
params: { sku: sku },
headers: { 'Authorization': `Bearer ${process.env.ACCOUNTING_API_KEY}` }
});
const accountingItem = searchResponse.data.items[0];
if (!accountingItem) {
console.warn(`SKU ${sku} not found in accounting software.`);
return res.status(404).send('Item not found in accounting software');
}
// 2. Push inventory adjustment to Accounting Software API
await axios.post(`https://api.accounting.com/v1/inventory-adjustments`, {
itemId: accountingItem.id,
locationId: location_id,
newQuantityOnHand: updated_quantity,
transactionDate: new Date().toISOString()
}, {
headers: {
'Authorization': `Bearer ${process.env.ACCOUNTING_API_KEY}`,
'Content-Type': 'application/json'
}
});
return res.status(200).json({ status: 'success', syncedSku: sku });
} catch (error) {
console.error('Sync failed:', error.response?.data || error.message);
// Return 500 so the e-commerce platform retries the webhook
return res.status(500).send('Failed to push inventory update');
}
});
app.listen(3000, () => console.log('Inventory sync service running on port 3000'));
Step 3: Handle Concurrency and API Rate Limits
Production environments require safeguards to maintain data integrity under heavy traffic:
- Message Queues: Push incoming webhooks into a queue (e.g., Redis BullMQ, RabbitMQ, or AWS SQS) before calling the accounting API. This throttles outbound calls to prevent hitting rate limits (e.g., QuickBooks' 500 requests per minute limit).
- Idempotency Controls: Store the unique event ID or high-resolution timestamp of each update. Ignore updates that arrive out of chronological order to prevent older stock counts from overwriting newer ones.
- Dead-Letter Queues (DLQ): Route failed sync requests (e.g., missing SKUs or deleted items) to a DLQ for administrator review rather than discarding them.
Need this done fast? order an integration on FreelanceHunt.
I take on freelance fixes and builds in this area.