Skip to main content

Product Management Guide

Manage product catalogs, variants, and retail orders.

This guide covers product categories and products, the axis-driven variant system, and the order lifecycle.

SDK conventions

TypeScript examples follow wiil-js (enums import from wiil-core-js); Python examples follow wiil. A product requires at least one variant for pricing — in TypeScript you create variants inline (and manage axes and variants as dedicated resources) and order items reference a variantId; in Python products are priced and stocked directly and order items reference a product_id + sku. Variant axes, product variants, and order status/cancel are exposed through the TypeScript SDK; those sections are labeled.

Quick start

import { WiilClient } from 'wiil-js';

const client = new WiilClient({ apiKey: process.env.WIIL_API_KEY! });

// 1. Create a category
const category = await client.products.createCategory({
name: 'Electronics',
description: 'Electronic devices and accessories',
isDefault: false,
});

// 2. Create a product with its required variants
const product = await client.products.create({
categoryId: category.id,
name: 'Wireless Mouse',
description: 'Ergonomic wireless mouse with 6 buttons',
price: 29.99,
sku: 'WM-2024-BLK',
trackInventory: true,
isActive: true,
isAlcoholic: false,
variants: [
{ axisValues: {}, price: 29.99, isDefault: true, isActive: true },
],
});

// 3. Place an order (items require variantId)
const order = await client.productOrders.create({
customerId: 'cust_123',
orderDate: Date.now(),
items: [
{
productId: product.id,
variantId: product.variants[0].id,
itemName: product.name,
quantity: 2,
unitPrice: 29.99,
totalPrice: 59.98,
},
],
pricing: { subtotal: 59.98, total: 59.98 },
});

console.log(`Order Created: ${order.id}`);

Product categories

Create, get, list, update, delete

const category = await client.products.createCategory({
name: 'Electronics',
description: 'Electronic devices and accessories',
isDefault: false,
});

const loaded = await client.products.getCategory('category_123');
const result = await client.products.listCategories();

const updated = await client.products.updateCategory({
id: 'category_123',
description: 'High-end electronic devices',
});

await client.products.deleteCategory('category_123');

Batch create categories

Up to 50 categories per request.

const categories = await client.products.createCategoryBatch([
{ name: 'Electronics', description: 'Electronic devices', isDefault: false },
{ name: 'Accessories', description: 'Computer peripherals', isDefault: false },
{ name: 'Software', description: 'Digital products', isDefault: false },
]);

Products

A product requires at least one variant. In TypeScript, create variants inline; in Python, set price and inventory directly.

Create a product

const tshirt = await client.products.create({
categoryId: 'category_apparel',
name: 'Classic T-Shirt',
description: 'Comfortable cotton t-shirt',
price: 24.99,
sku: 'TS-CLASSIC',
trackInventory: true,
isActive: true,
isAlcoholic: false,
variants: [
{ axisValues: { Size: 'Small', Color: 'Black' }, price: 24.99, isDefault: true, isActive: true },
{ axisValues: { Size: 'Medium', Color: 'Black' }, price: 24.99, isDefault: false, isActive: true },
{ axisValues: { Size: 'Large', Color: 'Black' }, price: 26.99, isDefault: false, isActive: true },
],
});

console.log(`Created ${tshirt.name} with ${tshirt.variants.length} variants`);

Get, update, delete

const product = await client.products.get('product_123');
const result = await client.products.list();

const updated = await client.products.update({ id: 'product_123', price: 179.99 });

await client.products.delete('product_123');

The Python SDK looks products up by SKU with get_by_sku; in TypeScript, SKU lookup is a product variant operation.

Batch create products

Up to 100 products per request.

const products = await client.products.createBatch([
{
categoryId: 'cat_accessories',
name: 'Wireless Mouse',
description: 'Ergonomic wireless mouse',
price: 29.99,
sku: 'WM-001',
trackInventory: true,
isActive: true,
isAlcoholic: false,
variants: [{ axisValues: {}, price: 29.99, isDefault: true, isActive: true }],
},
{
categoryId: 'cat_accessories',
name: 'Mechanical Keyboard',
description: 'RGB backlit mechanical keyboard',
price: 89.99,
sku: 'KB-001',
trackInventory: true,
isActive: true,
isAlcoholic: false,
variants: [{ axisValues: {}, price: 89.99, isDefault: true, isActive: true }],
},
]);

Variant axes

Define reusable axes (Size, Color, Storage) once at the organization level. (TypeScript SDK)

import { VariantAxisType } from 'wiil-core-js';

const sizeAxis = await client.productVariantAxes.create({
name: 'Size',
type: VariantAxisType.TEXT,
values: [
{ id: 'sm', label: 'Small', sortOrder: 0 },
{ id: 'md', label: 'Medium', sortOrder: 1 },
{ id: 'lg', label: 'Large', sortOrder: 2 },
],
isActive: true,
});

const colorAxis = await client.productVariantAxes.create({
name: 'Color',
type: VariantAxisType.SWATCH,
values: [
{ id: 'black', label: 'Black', swatchColor: '#000000', sortOrder: 0 },
{ id: 'white', label: 'White', swatchColor: '#FFFFFF', sortOrder: 1 },
],
isActive: true,
});

const axis = await client.productVariantAxes.get('axis_123');
const byName = await client.productVariantAxes.getByName('Size');
const all = await client.productVariantAxes.list();
await client.productVariantAxes.update('axis_123', { id: 'axis_123', values: [/* ... */] });
await client.productVariantAxes.delete('axis_123');

Product variants

A variant is a SKU — a combination of axis values with its own price and stock. (TypeScript SDK)

const variant = await client.productVariants.create({
productId: 'product_123',
axisValues: { Size: 'Extra Large', Color: 'Blue' },
sku: 'TS-XL-BL',
price: 29.99,
isDefault: false,
isActive: true,
});

const loaded = await client.productVariants.get('variant_123');
const bySku = await client.productVariants.getBySku('TS-XL-BL');
const defaultVariant = await client.productVariants.getDefault('product_123');

const updated = await client.productVariants.update('variant_123', {
id: 'variant_123',
price: 32.99,
isActive: true,
});

const batch = await client.productVariants.createBatch([
{ productId: 'product_123', axisValues: { Size: 'Medium', Color: 'Red' }, sku: 'TS-MD-RD', price: 24.99, isDefault: false, isActive: true },
{ productId: 'product_123', axisValues: { Size: 'Large', Color: 'Green' }, sku: 'TS-LG-GN', price: 26.99, isDefault: false, isActive: true },
]);

await client.productVariants.delete('variant_123');

Product orders

Create an order

In TypeScript each order item references a variantId; in Python each references a product_id + sku.

const order = await client.productOrders.create({
customerId: 'cust_456',
orderDate: Date.now(),
items: [
{
productId: 'product_123',
variantId: 'variant_456',
itemName: 'Wireless Headphones',
quantity: 1,
unitPrice: 199.99,
totalPrice: 199.99,
},
],
pricing: { subtotal: 199.99, total: 199.99 },
});

Read orders

const order = await client.productOrders.get('order_123');
console.log(`${order.status} — $${order.pricing.total}`);

const result = await client.productOrders.list();
result.data.forEach(o => console.log(`- ${o.id}: ${o.status}`));

Update, status, cancel, delete

Order updates, status transitions, cancellation, and deletion are exposed through the TypeScript SDK.

import { OrderStatus } from 'wiil-core-js';

const updated = await client.productOrders.update({
id: 'order_123',
pricing: { subtotal: 180.00, total: 180.00 },
});

const advanced = await client.productOrders.updateStatus('order_123', {
id: 'order_123',
status: OrderStatus.CONFIRMED,
});

const cancelled = await client.productOrders.cancel('order_123', {
cancelReason: 'Customer requested cancellation',
});

await client.productOrders.delete('order_123');

Enums

import { OrderStatus, VariantAxisType } from 'wiil-core-js';

// OrderStatus: 'pending' | 'confirmed' | 'preparing' | 'ready'
// | 'out_for_delivery' | 'completed' | 'cancelled' | 'returned'
// VariantAxisType: 'text' | 'swatch' (also 'image', 'numeric')

The standard order lifecycle is pending → confirmed → preparing → ready → out_for_delivery → completed. Payment status tracks separately: pending, paid, partial, failed, refunded. Per-variant stock resolves to in_stock, low_stock, or out_of_stock.

Complete example: electronics store

import { WiilClient } from 'wiil-js';
import { OrderStatus, VariantAxisType, PreferredContactMethod } from 'wiil-core-js';

async function setupElectronicsStore() {
const client = new WiilClient({ apiKey: process.env.WIIL_API_KEY! });

// 1. Customer
const customer = await client.customers.create({
phone_number: '+15551234567',
firstname: 'John',
lastname: 'Doe',
preferred_language: 'en',
preferred_contact_method: PreferredContactMethod.EMAIL,
isValidatedNames: false,
});

// 2. Categories
const computers = await client.products.createCategory({ name: 'Computers', description: 'Laptops and desktops', isDefault: false });
const accessories = await client.products.createCategory({ name: 'Accessories', description: 'Computer peripherals', isDefault: false });

// 3. A reusable color axis
const colorAxis = await client.productVariantAxes.create({
name: 'Color',
type: VariantAxisType.SWATCH,
values: [
{ id: 'black', label: 'Black', swatchColor: '#000000', sortOrder: 0 },
{ id: 'silver', label: 'Silver', swatchColor: '#C0C0C0', sortOrder: 1 },
],
isActive: true,
});

// 4. Products with variants
const laptop = await client.products.create({
categoryId: computers.id,
name: 'Pro Laptop 15"',
description: '15-inch laptop with 16GB RAM',
price: 1299.99,
sku: 'LT-PRO-15',
trackInventory: true,
isActive: true,
isAlcoholic: false,
variants: [
{ axisValues: { Color: 'Black' }, price: 1299.99, isDefault: true, isActive: true },
{ axisValues: { Color: 'Silver' }, price: 1349.99, isDefault: false, isActive: true },
],
});

const mouse = await client.products.create({
categoryId: accessories.id,
name: 'Wireless Mouse',
description: 'Ergonomic wireless mouse',
price: 29.99,
sku: 'MS-WL',
trackInventory: true,
isActive: true,
isAlcoholic: false,
variants: [{ axisValues: {}, price: 29.99, isDefault: true, isActive: true }],
});

// 5. Order
const order = await client.productOrders.create({
customerId: customer.id,
orderDate: Date.now(),
items: [
{ productId: laptop.id, variantId: laptop.variants[0].id, itemName: 'Pro Laptop 15" (Black)', quantity: 1, unitPrice: 1299.99, totalPrice: 1299.99 },
{ productId: mouse.id, variantId: mouse.variants[0].id, itemName: 'Wireless Mouse', quantity: 2, unitPrice: 29.99, totalPrice: 59.98 },
],
pricing: { subtotal: 1359.97, total: 1359.97 },
});

// 6. Advance fulfillment
await client.productOrders.updateStatus(order.id, { id: order.id, status: OrderStatus.CONFIRMED });
await client.productOrders.updateStatus(order.id, { id: order.id, status: OrderStatus.PREPARING });
await client.productOrders.updateStatus(order.id, { id: order.id, status: OrderStatus.COMPLETED });

return { customer, categories: [computers, accessories], products: [laptop, mouse], order };
}

setupElectronicsStore().catch(console.error);

Best practices

  • Always include variants (TS) / price & stock (Python) — a product must be priced. In TypeScript every product needs at least one variant; order items then reference a variantId.
  • Define axes once — create reusable productVariantAxes (Size, Color) and reference their values in variants.
  • Track stock per variant — each SKU carries its own stock for accurate fulfillment; mark isAlcoholic to trigger age verification in the order flow.
  • Follow the status lifecyclepending → confirmed → preparing → ready → out_for_delivery → completed; record a cancelReason when cancelling.

Next steps


← Back to Guides