Skip to content

Products and Inventory

Mehrdad Pajuhaan edited this page Oct 23, 2026 · 1 revision

Products and Inventory

Practical recipes for the product side of a vendor panel: reading the catalog page by page, browsing category folders, updating stock, organizing products, managing variants and importing products in bulk. Every example assumes SelldoneCore.Setup() and VendorSDK.Setup() have run and VENDOR_ID is the signed-in vendor's ID (see Getting Started).

Read all products, page by page

async function fetchAllProducts(vendorId, pageSize = 100) {
  const all = [];
  let offset = 0;
  let total = Infinity;

  while (offset < total) {
    const res = await window.$vendor.product.list(vendorId, offset, pageSize, {
      products_only: true,
      with_total: true,
    });
    all.push(...res.products);
    total = res.total;
    offset += pageSize;
    if (!res.products.length) break;
  }
  return all;
}

Browse category folders

Without products_only, a list call returns the folders (categories) of the current directory together with its products. Pass dir to open a folder and with_parent to get the breadcrumb:

const { folders, products, parent_folders } = await window.$vendor.product.list(
  VENDOR_ID, 0, 50, { dir: CATEGORY_ID, with_parent: true },
);

To list categories on their own, with children and parents:

const { categories, total } = await window.$vendor.category.list(VENDOR_ID, 0, 100, {
  children: true,
  parent: true,
});

Read one product

const { product, product_data, orderQue } =
  await window.$vendor.product.getInfo(VENDOR_ID, PRODUCT_ID, 0, 10);

console.log(product.title, product.product_variants.length, product.images.length);

Update stock

For a product without variants:

const { quantity } = await window.$vendor.product.setQuantity(VENDOR_ID, PRODUCT_ID, 40);

For one variant, the URL builder accepts an optional variant_id:

const { data } = await window.axios.post(
  window.VAPI.POST_MY_VENDOR_PRODUCT_QUANTITY(VENDOR_ID, PRODUCT_ID, VARIANT_ID),
  { quantity: 12 },
);

Move products to another category

// Move one product
await window.$vendor.product.changeCategory(VENDOR_ID, PRODUCT_ID, CATEGORY_ID);

// Move several products together, or move to the root with null
const { count } = await window.$vendor.product.changeCategory(
  VENDOR_ID, PRODUCT_ID, null, [OTHER_PRODUCT_ID_1, OTHER_PRODUCT_ID_2],
);

Tag products

const { tags } = await window.$vendor.product.tags.set(VENDOR_ID, PRODUCT_ID, ["summer", "eco"]);

Pass null or an empty array to clear the tags.

Remove and restore variants

const removed = await window.$vendor.product.variants.remove(VENDOR_ID, PRODUCT_ID, VARIANT_ID);
// removed.variants: the remaining variants, removed.quantity: the new product quantity

const restored = await window.$vendor.product.variants.restore(VENDOR_ID, PRODUCT_ID, VARIANT_ID);

Import products in bulk

The importer accepts a spreadsheet-style dataset. The first row holds the column headers:

const dataset = [
  ["Title", "Type", "Price", "Currency", "Status", "Quantity", "SKU", "Category", "Image"],
  ["Linen Shirt", "PHYSICAL", 39, "USD", "Open", 25, "LIN-001", "Shirts", "https://example.com/linen.jpg"],
  ["Cotton Tee", "PHYSICAL", 19, "USD", "Open", 80, "COT-001", "Shirts", "https://example.com/tee.jpg"],
];

const { success_count, fail_count, errors } =
  await window.$vendor.product.importer.send(VENDOR_ID, dataset, true); // keep_image_urls

Then monitor the import and its image queue:

const { importing, images } = await window.$vendor.product.importer.info(VENDOR_ID);

const queue = await window.$vendor.product.importer.que.images(VENDOR_ID, 0, 20, {
  sortBy: "created_at",
  sortDesc: true,
  search: "",
});

// Retry or discard a queued image
await window.$vendor.product.importer.que.syncImage(VENDOR_ID, queue.images[0].id);
await window.$vendor.product.importer.que.removeImage(VENDOR_ID, queue.images[0].id);

que.products() and que.removeProduct() work the same way for queued product rows.

Vendor offers (inventory)

A vendor's offers on marketplace products (price, stock and lead time per product or variant) are listed and updated through the inventory routes. They have URL builders but no typed wrappers:

Action URL builder Route
List offers GET_MY_VENDOR_INVENTORY_PRODUCTS(vendor_id) GET /vendors/{vendor_id}/inventory
Update an offer PUT_MY_VENDOR_INVENTORY_UPDATE_PRODUCT(vendor_id, vendor_product_id) PUT /vendors/{vendor_id}/inventory/{vendor_product_id}
Remove an offer DELETE_MY_VENDOR__INVENTORY_PRODUCT(vendor_id, vendor_product_id) DELETE /vendors/{vendor_id}/inventory/{vendor_product_id}
Export inventory GET_MY_VENDOR_EXPORT_INVENTORY(vendor_id, file) GET /vendors/{vendor_id}/inventory/{file}

To see which vendors offer a product, use window.$vendor.product.vendor.list(VENDOR_ID, PRODUCT_ID, 0, 20).

Images

Product image routes are available as URL builders: POST_MY_VENDOR_UPLOAD_PRODUCT_MAIN_IMAGE, POST_MY_VENDOR_UPLOAD_PRODUCT_COVER (with an optional variant_id), PUT_MY_VENDOR_PRODUCT_IMAGES_ORDER, PUT_MY_VENDOR_PRODUCT_IMAGE_ALT and DELETE_MY_VENDOR_PRODUCT_IMAGE. The owner-side equivalents are documented in Add Images to Products/Variants.

Related