Repository navigation
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).
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;
}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,
});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);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 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],
);const { tags } = await window.$vendor.product.tags.set(VENDOR_ID, PRODUCT_ID, ["summer", "eco"]);Pass null or an empty array to clear the tags.
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);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_urlsThen 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.
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).
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.