
Multi Channel Selling
- 78 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Sync catalog and inventory across your own site, Amazon, eBay, and wholesale channels to sell everywhere from one system.
About
Syncs catalog and inventory across a store's own site plus Amazon, eBay, and wholesale channels from a single source of truth. A developer uses it to sell on multiple channels without inventory drift.
- Unified catalog and inventory across owned site and marketplaces
- Single-system management of multiple sales channels
Multi Channel Selling by the numbers
- 78 all-time installs (skills.sh)
- Ranked #905 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill multi-channel-sellingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 78 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Sync catalog and inventory across your own site, Amazon, eBay, and wholesale channels to sell everywhere from one system.
Files
Multi-Channel Selling
Overview
Multi-channel selling lets you list products on your own website, Amazon, eBay, Walmart, and wholesale portals simultaneously — with inventory synchronized in real time so you never oversell. The critical rule: one inventory pool, updated immediately when any channel sells or restocks. Purpose-built channel management tools (Linnworks, Sellbrite, Skubana/Extensiv) make this manageable without custom integration work.
When to Use This Skill
- When expanding beyond your own website to sell on Amazon, eBay, or Walmart Marketplace
- When running both a retail DTC site and a wholesale B2B portal from the same inventory pool
- When selling the same products under different brand names or content across channels
- When overselling on one channel because inventory is not shared in real time
- When building a channel management platform that lets brands manage all their sales channels in one place
Core Instructions
Step 1: Determine your platform and choose the right channel management tool
| Platform | Recommended Tool | Why |
|---|---|---|
| Shopify | Shopify's native Sales Channels + Sellbrite or Linnworks for external marketplaces | Shopify has built-in Facebook, Instagram, Google, and TikTok channels; Sellbrite adds Amazon/eBay/Walmart with inventory sync |
| WooCommerce | WP-Lister Pro (Amazon + eBay) or Linnworks | WP-Lister Pro lists products from WooCommerce directly to Amazon/eBay and syncs orders back |
| BigCommerce | BigCommerce Channels + Codisto (for Amazon/eBay) | BigCommerce has a native Channel Manager; Codisto extends it to Amazon, eBay, and Walmart with real-time sync |
| Any Platform | Linnworks or Skubana (Extensiv) as a central OMS | These tools sit above all channels and act as the single source of inventory truth for high-volume multi-channel operations |
Step 2: Set up your primary store as the inventory master
Before connecting any channels, ensure your primary store has:
- Accurate stock levels for all SKUs
- Product dimensions and weights entered (required for marketplace listings)
- HS codes if selling internationally
- UPC/GTIN barcodes on all products (required for Amazon and Walmart listings)
Shopify
1. Go to Settings → Sales channels — Shopify lists all available channels 2. Add Facebook & Instagram (free, by Meta) to sync products to your Facebook Shop and Instagram Shopping 3. Add Google & YouTube (free, by Google) to sync products to Google Shopping 4. For Amazon: install Codisto or Sellbrite from the Shopify App Store — these handle the Amazon SP-API integration
WooCommerce
1. Your WooCommerce store is the inventory master 2. Install WP-Lister Pro for Amazon and/or WP-Lister Pro for eBay from wpla.net 3. WP-Lister reads your WooCommerce products and creates Amazon/eBay listings from them 4. Orders from Amazon/eBay are imported back to WooCommerce automatically
BigCommerce
1. Go to Channel Manager in BigCommerce admin (available on all plans) 2. Connect Google Shopping, Facebook, Instagram via the native channel connectors 3. For Amazon and eBay: install Codisto from the BigCommerce App Marketplace — it's the recommended Amazon/eBay integration for BigCommerce
Step 3: Connect marketplace channels
Amazon
Amazon requires a Professional Seller account ($39.99/month) and approved product categories. You'll also need GTINs (UPCs or ASINs) for every listing.
Via Shopify + Sellbrite: 1. Install Sellbrite from the Shopify App Store 2. Connect your Amazon Seller Central account in Sellbrite → Channels → Add Channel → Amazon 3. Sellbrite imports your existing Amazon ASINs if you already have listings, or you can create new listings from your Shopify products 4. Enable inventory sync: Sellbrite pushes your Shopify inventory quantity to Amazon in real time (within minutes of a sale on either channel) 5. Amazon orders import into Sellbrite and sync to Shopify as new orders automatically
Via WooCommerce + WP-Lister: 1. Connect your Amazon Seller Central account in WP-Lister Pro → Amazon → Settings 2. Map your WooCommerce products to ASINs (match by UPC/GTIN or search Amazon's catalog) 3. WP-Lister syncs inventory from WooCommerce to Amazon and imports Amazon orders to WooCommerce
eBay
Via Shopify + Sellbrite: 1. In Sellbrite, go to Channels → Add Channel → eBay 2. Connect your eBay Seller account via OAuth 3. Map Shopify products to eBay listing templates; Sellbrite handles category requirements and eBay-specific fields 4. Inventory syncs both ways in near real time
Via WooCommerce + WP-Lister: 1. Same setup as Amazon — connect your eBay account in WP-Lister Pro → eBay → Settings 2. Create listing templates in WP-Lister to map WooCommerce product data to eBay category requirements
Walmart Marketplace
Via Shopify + Sellbrite: 1. Walmart Marketplace requires an application and approval (apply at marketplace.walmart.com) 2. Once approved: connect Walmart in Sellbrite → Channels → Add Channel → Walmart 3. Sellbrite handles Walmart's specific listing requirements (requires GTIN, specific image dimensions)
Via BigCommerce + Codisto: 1. Apply for Walmart Marketplace approval separately 2. Once approved: connect Walmart in Codisto → Channels → Walmart 3. Codisto syncs products and inventory automatically
Step 4: Configure inventory allocation per channel
When one channel sells out, you want to prevent other channels from showing stock you don't have. Most channel management tools handle this — configure buffer quantities per channel.
In Sellbrite: 1. Go to Sellbrite → Channels → [Channel] → Inventory Settings 2. Set "Channel Buffer": e.g., keep 5 units in reserve so Shopify always has stock even if Amazon buys the rest 3. Enable "Stop selling when inventory = 0" to prevent oversells
In Linnworks: 1. Linnworks → Settings → Stock Management → Configure per-channel stock allocation 2. Set safety stock levels per channel to prevent one channel from consuming all inventory
In Codisto (BigCommerce): 1. Codisto → Inventory → Configure "Available stock formula": e.g., BigCommerce stock - 3 for Amazon (holds 3 units back for other channels)
Step 5: Handle orders from all channels
Every channel order should flow into your central system and trigger fulfillment from the same process.
Shopify with Sellbrite:
- Amazon and eBay orders import to Sellbrite and sync to Shopify as regular orders
- Fulfill in Shopify normally — ShipStation or Shopify Shipping handles label creation
- Sellbrite pushes tracking numbers back to Amazon/eBay automatically (required within 2 business days for Amazon)
WooCommerce with WP-Lister:
- Amazon and eBay orders import to WooCommerce as regular orders
- Fulfill in WooCommerce (or ShipStation); tracking numbers sync back to the marketplace automatically
Linnworks (any platform):
- All orders from all channels appear in Linnworks → Orders in one view
- Create pick lists and shipping labels in Linnworks
- Linnworks pushes fulfillment confirmation and tracking to each channel automatically
Custom / Headless — cross-channel order ingestion
// Normalize an order from any marketplace into a common format
interface NormalizedMarketplaceOrder {
channelName: string; // 'amazon', 'ebay', 'walmart', 'shopify'
channelOrderId: string; // the marketplace's order ID
lines: {
channelSku: string;
masterSku: string; // your internal SKU
quantity: number;
unitPriceCents: number;
}[];
shippingAddress: Address;
customerEmail: string;
}
async function ingestMarketplaceOrder(order: NormalizedMarketplaceOrder): Promise<void> {
// Idempotency: skip if already imported
const existing = await db.orders.findByChannelOrderId(order.channelName, order.channelOrderId);
if (existing) return;
await db.transaction(async tx => {
// Create the order in your system
const createdOrder = await tx.orders.insert({
channel: order.channelName,
channel_order_id: order.channelOrderId,
status: 'awaiting_fulfillment',
shipping_address: order.shippingAddress,
customer_email: order.customerEmail,
});
// Reserve inventory for each line
for (const line of order.lines) {
await tx.orderLines.insert({
order_id: createdOrder.id,
sku: line.masterSku,
quantity: line.quantity,
unit_price_cents: line.unitPriceCents,
});
// Decrement inventory immediately
await tx.raw(
'UPDATE inventory SET reserved = reserved + ? WHERE sku = ? AND (quantity_on_hand - reserved) >= ?',
[line.quantity, line.masterSku, line.quantity]
);
}
});
}Step 6: Monitor cross-channel performance
Track per-channel metrics monthly to understand your channel economics:
| Metric | Where to find it |
|---|---|
| Revenue per channel | Sellbrite/Linnworks Reports → Sales by Channel |
| Sell-through rate per channel | Compare starting inventory to units sold per period |
| Inventory sync lag (stale listings) | Check your channel tool's "last synced" timestamp — anything over 30 minutes needs investigation |
| Return rate per channel | Amazon has its own return dashboard; WooCommerce/Shopify show returns by source channel tag |
Best Practices
- Never maintain separate stock counts per channel — one pool, updated immediately after every sale on any channel; stale inventory leads to oversells
- Use channel-specific SKU mappings — Amazon uses ASINs, eBay uses item IDs, your store uses your internal SKU; maintain a mapping table rather than exposing your internal SKUs to marketplaces
- Set safety stock buffers per channel — allocate a reserve so one channel can't sell out your entire inventory before the sync propagates to other channels
- Test with a single product before scaling — connect one product to Amazon, verify the listing, place a test order, and confirm inventory syncs before connecting your entire catalog
- Log all channel sync failures with retry — marketplace API calls fail intermittently; use your channel management tool's retry logic and review the failure log weekly
Common Pitfalls
| Problem | Solution |
|---|---|
| Oversell when two channels receive orders simultaneously | Use atomic inventory reservation with a WHERE clause checking available qty; Sellbrite/Linnworks handle this; for custom builds use database-level atomic updates |
| Amazon listing goes inactive after a price change | Amazon may require a price change notification or your listing could violate their pricing policies; check the Amazon Seller Central notification inbox immediately when listings go inactive |
| Duplicate order imports when webhook fires twice | Add a unique constraint on (channel_name, channel_order_id) in your orders table and use INSERT ... ON CONFLICT DO NOTHING |
| Tracking number not pushed back to marketplace | Amazon requires tracking within 2 business days or seller performance metrics drop; verify your channel tool's automatic fulfillment confirmation is enabled |
Related Skills
- @order-management-system
- @vendor-management
- @b2b-commerce
- @dropshipping-integration
- @marketplace-building
{
"context": "Tests whether the agent designs a channel-agnostic catalog schema with the correct column types, constraints, and table structure, including UUID keys, integer pricing in cents, JSONB for dimensions, TIMESTAMPTZ for timestamps, the correct channel type enum check, and proper uniqueness constraints on channel_listings and orders.",
"type": "weighted_checklist",
"checklist": [
{
"name": "UUID with gen_random_uuid()",
"max_score": 8,
"description": "Product (and other entity) primary keys use UUID type with DEFAULT gen_random_uuid()"
},
{
"name": "master_sku unique",
"max_score": 8,
"description": "Products table has a master_sku column declared as NOT NULL UNIQUE (VARCHAR or TEXT)"
},
{
"name": "Price stored in integer cents",
"max_score": 10,
"description": "Price in channel_listings (or equivalent) is stored as INTEGER, not DECIMAL/FLOAT/NUMERIC, with cents as the unit"
},
{
"name": "JSONB dimensions",
"max_score": 8,
"description": "Product dimensions are stored in a JSONB column (not separate columns or TEXT)"
},
{
"name": "TIMESTAMPTZ timestamps",
"max_score": 8,
"description": "All timestamp columns use TIMESTAMPTZ (not TIMESTAMP without timezone)"
},
{
"name": "Channel type CHECK constraint",
"max_score": 10,
"description": "Channels table has a CHECK constraint limiting type to 'dtc', 'wholesale', 'marketplace', and 'pos'"
},
{
"name": "Unique channel SKU per channel",
"max_score": 10,
"description": "channel_listings table has a UNIQUE constraint on (channel_id, channel_sku) combination"
},
{
"name": "Single shared inventory pool",
"max_score": 10,
"description": "Inventory is stored in a single table with a shared reserved count (not split per channel), and notes.md or comments explain that reserved spans all channels"
},
{
"name": "Non-negative inventory constraint",
"max_score": 8,
"description": "Inventory table has a CHECK constraint ensuring quantity_on_hand >= 0"
},
{
"name": "Channel inventory allocations table",
"max_score": 10,
"description": "Schema includes a channel_inventory_allocations (or equivalent) table for optional per-channel buffers, with allocated_qty defaulting to 0"
},
{
"name": "Unique channel order ID constraint",
"max_score": 10,
"description": "Orders table (if included) has a UNIQUE constraint on (channel_id, channel_order_id) to prevent duplicate imports"
}
]
}
Unified Multi-Channel Catalog Database Schema
Problem/Feature Description
Finboard is a growing consumer electronics brand that currently sells exclusively through their own website. After a strong holiday season, their growth team has secured approval to expand to Amazon US and Walmart Marketplace. Their existing product table is tightly coupled to their DTC storefront — it stores a single price, a single product description, and has no concept of per-channel availability or external marketplace identifiers.
The engineering team needs to design a new PostgreSQL schema that will serve as the backbone for unified catalog and inventory management across all three channels. The schema must allow them to maintain a single authoritative product record internally while supporting channel-specific pricing, titles, and stock availability. It also needs to track inventory in a way that prevents overselling when orders arrive from multiple channels simultaneously. The schema will be used as the foundation before any application code is written.
Output Specification
Produce a single SQL file named schema.sql containing all CREATE TABLE statements for the complete multi-channel catalog and inventory schema. Include all constraints, indexes, and default values.
Also produce a notes.md file with brief explanations of any non-obvious design decisions in your schema, including your approach to storing monetary values, how inventory state is shared across channels, and how you've guarded against data integrity issues at the database level.
{
"context": "Tests whether the agent implements inventory reservation using an atomic database UPDATE with availability check in the WHERE clause, wraps it in a transaction, uses Promise.allSettled for parallel channel pushes, respects per-channel allocations, skips pushing to DTC, updates last_synced_at, and documents real-time event triggering.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Atomic reservation UPDATE",
"max_score": 15,
"description": "reserveInventory uses an UPDATE statement that increments reserved AND checks (quantity_on_hand - reserved) >= quantity in the same WHERE clause (not a separate SELECT then UPDATE)"
},
{
"name": "Transaction wrapping",
"max_score": 8,
"description": "reserveInventory wraps the UPDATE and any subsequent inserts inside a database transaction"
},
{
"name": "Promise.allSettled for fan-out",
"max_score": 10,
"description": "pushInventoryToChannels uses Promise.allSettled() (not Promise.all()) when iterating over channel listings"
},
{
"name": "Per-channel allocation cap",
"max_score": 12,
"description": "When pushing to a channel, the quantity is capped at allocated_qty when that value is greater than 0 (using Math.min or equivalent), otherwise the full available quantity is used"
},
{
"name": "DTC skipped in push",
"max_score": 10,
"description": "The DTC channel is explicitly skipped (no push call) during the channel sync fan-out"
},
{
"name": "last_synced_at updated",
"max_score": 8,
"description": "After a successful push to each channel, the code updates last_synced_at on the channel_listings row"
},
{
"name": "Event-driven sync triggers",
"max_score": 12,
"description": "design-notes.md states that inventory syncs should be triggered on order reservation, fulfillment, AND return events (not only on a schedule)"
},
{
"name": "Real-time vs scheduled",
"max_score": 10,
"description": "design-notes.md explicitly explains why relying solely on scheduled jobs is insufficient (stale inventory / oversell risk)"
},
{
"name": "Safety stock buffers mentioned",
"max_score": 8,
"description": "design-notes.md or code comments mention that per-channel allocated_qty buffers prevent a single high-velocity channel from draining all shared stock"
},
{
"name": "Error on insufficient inventory",
"max_score": 7,
"description": "reserveInventory throws an error (or equivalent) when the atomic UPDATE affects 0 rows (i.e. insufficient available inventory)"
}
]
}
Real-Time Inventory Synchronization Across Channels
Problem/Feature Description
Meadowbrook Outdoors sells camping gear through their own DTC website, Amazon US, and Walmart Marketplace. During peak season last year, their operations team found that Amazon and Walmart were both showing 12 units available for a popular tent while only 8 were physically in stock. When both channels sold units at the same time, they ended up with 4 oversell incidents that required manual cancellations and refund processing — a nightmare for customer satisfaction scores.
Their engineering team has just migrated to a unified PostgreSQL database using a shared inventory table (with quantity_on_hand and reserved columns) alongside a channel_listings table and a channel_inventory_allocations table that lets certain channels be capped at a maximum allocation. They now need TypeScript service functions to (a) safely reserve inventory when an order arrives from any channel, and (b) push updated available quantities to each active channel listing after any stock change.
Output Specification
Produce a inventory-service.ts file containing: 1. A reserveInventory(productId, channelId, quantity, orderId) function 2. A pushInventoryToChannels(productId) function that fans out to all active channel listings
You may stub out the external marketplace sync calls (e.g. syncAmazonInventory, syncWalmartInventory) and database access (e.g. db.inventory.findByProductId) as simple async function stubs — focus on the logic and control flow.
Also produce a design-notes.md explaining when inventory syncs should be triggered (what events should cause a sync to fire) and why using only a nightly scheduled job would be insufficient.
{
"context": "Tests whether the agent implements per-channel adapter classes that normalize marketplace orders to a shared interface, resolves channel SKUs to master product IDs via channel_listings, implements idempotency, avoids exposing master SKUs in channel-facing code, and describes a retry strategy with exponential backoff.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Shared NormalizedOrder interface",
"max_score": 10,
"description": "types.ts (or equivalent) defines a shared interface/type for normalized orders containing at minimum: a channel order ID, channel ID, order lines (with channel SKU, quantity, price), shipping address, and customer email"
},
{
"name": "Per-channel adapter separation",
"max_score": 12,
"description": "Amazon and Walmart adapters are implemented as separate modules/files — marketplace-specific parsing logic is NOT mixed into the core order-ingestion.ts file"
},
{
"name": "Channel SKU to master product resolution",
"max_score": 12,
"description": "order-ingestion.ts resolves channel SKUs to internal product IDs by looking up channel_listings (or equivalent mapping table) — it does NOT look up products directly by the channel SKU"
},
{
"name": "Master SKUs not exposed externally",
"max_score": 8,
"description": "Adapter outputs and normalized order types use channel SKUs (not master/internal SKUs) as the line-item identifier passed across the channel boundary"
},
{
"name": "Idempotency check on ingestion",
"max_score": 12,
"description": "order-ingestion.ts checks whether an order with the same channelId + channelOrderId already exists and returns early (without re-inserting) if it does"
},
{
"name": "Error on unknown channel SKU",
"max_score": 8,
"description": "order-ingestion.ts throws an error (or rejects/returns an error) when a channel SKU cannot be found in the listings table"
},
{
"name": "Inventory reservation in transaction",
"max_score": 8,
"description": "The ingestion function calls inventory reservation inside (or wrapped in) a database transaction alongside order creation"
},
{
"name": "Exponential backoff in retry strategy",
"max_score": 10,
"description": "retry-strategy.md describes using exponential backoff (not fixed-interval retries) for outbound marketplace API call failures"
},
{
"name": "Queue-based retry",
"max_score": 10,
"description": "retry-strategy.md describes using a queue (job queue, message queue, or task queue) to persist and retry failed sync operations, rather than retrying inline"
},
{
"name": "Failure logging",
"max_score": 10,
"description": "retry-strategy.md mentions logging sync failures with enough detail to diagnose and replay them"
}
]
}
Marketplace Webhook Order Ingestion
Problem/Feature Description
Crafter's Depot is an arts-and-crafts retailer that just completed their marketplace expansion, adding Amazon US and Walmart Marketplace alongside their existing DTC store. Their webhook handlers are a mess: Amazon-specific parsing logic is mixed into the order processing code, Walmart's format is handled inline with a bunch of if (source === 'walmart') branches scattered throughout, and they have had multiple incidents where the same order got imported twice when a marketplace webhook fired more than once. Their engineers have also reported that new marketplace onboarding takes weeks because each new channel requires touching many unrelated files.
The team wants to refactor this into a clean, maintainable ingestion layer. Orders from each marketplace arrive via HTTP webhook in that marketplace's native format. The system must normalize them before processing, look up internal product records from marketplace-facing identifiers, and atomically reserve inventory for each line item. The architecture should make it easy to add a new marketplace (like eBay) in the future without touching core order processing logic.
Output Specification
Produce the following TypeScript files:
types.ts— shared type definitions for the normalized order formatadapters/amazon.ts— adapter that converts an Amazon order payload to the normalized formatadapters/walmart.ts— adapter that converts a Walmart order payload to the normalized formatorder-ingestion.ts— core ingestion function that accepts a normalized order, resolves channel SKUs to internal product IDs, creates the order record, and reserves inventory
You may stub database access and reserveInventory as simple stubs. Include realistic but simplified example payloads (inlined as constants) for both the Amazon and Walmart formats to demonstrate what normalization is doing.
Also produce a retry-strategy.md document explaining how the system should handle failures when calling outbound marketplace APIs (e.g. confirming an order or updating stock), covering retry behavior and failure logging.
{
"name": "finsi/multi-channel-selling",
"version": "0.1.0",
"summary": "Unified catalog and inventory across DTC, wholesale, marketplace channels",
"skills": {
"multi-channel-selling": {
"path": "SKILL.md"
}
}
}