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.
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
- TypeScript
- Python
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}`);
import os
from time import time
from wiil import WiilClient
from wiil.models.business_mgt import (
CreateBusinessProduct,
CreateProductCategory,
CreateProductOrder,
OrderAddress,
OrderPricing,
ProductOrderItemBase,
)
client = WiilClient(api_key=os.environ["WIIL_API_KEY"])
now_ms = int(time() * 1000)
category = client.products.create_category(
CreateProductCategory(name="Electronics", description="Electronic devices and accessories", display_order=1)
)
product = client.products.create(
CreateBusinessProduct(
name="Wireless Mouse",
description="Ergonomic wireless mouse with 6 buttons",
price=29.99,
sku="WM-2024-BLK",
barcode="123456789012",
category_id=category.id,
brand="TechBrand",
track_inventory=True,
stock_quantity=150,
low_stock_threshold=20,
weight=0.25,
is_active=True,
)
)
order = client.product_orders.create(
CreateProductOrder(
items=[
ProductOrderItemBase(
product_id=product.id,
item_name=product.name,
sku=product.sku,
quantity=2,
unit_price=product.price,
total_price=product.price * 2,
)
],
customer_id="cust_123",
pricing=OrderPricing(subtotal=59.98, tax=4.80, shipping_amount=9.99, total=74.77, currency="USD"),
order_date=now_ms,
shipping_address=OrderAddress(
street="123 Main St", city="New York", state="NY", postal_code="10001", country="US"
),
source="web",
)
)
print("Created product:", product.id)
print("Created order:", order.id)
Product categories
Create, get, list, update, delete
- TypeScript
- Python
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');
from wiil.models.business_mgt import CreateProductCategory, UpdateProductCategory
from wiil.types import PaginationRequest
category = client.products.create_category(
CreateProductCategory(name="Accessories", description="Add-ons")
)
loaded = client.products.get_category(category.id)
categories = client.products.list_categories(PaginationRequest(page=1, page_size=20))
updated = client.products.update_category(
UpdateProductCategory(id=category.id, name="Premium Accessories")
)
client.products.delete_category(updated.id)
Batch create categories
Up to 50 categories per request.
- TypeScript
- Python
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 },
]);
from wiil.models.business_mgt import CreateProductCategory
categories = client.products.create_category_batch([
CreateProductCategory(name="Electronics", display_order=1),
CreateProductCategory(name="Clothing", display_order=2),
CreateProductCategory(name="Accessories", display_order=3),
])
Products
A product requires at least one variant. In TypeScript, create variants inline; in Python, set price and inventory directly.
Create a product
- TypeScript
- Python
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`);
from wiil.models.business_mgt import CreateBusinessProduct
product = client.products.create(
CreateBusinessProduct(
name="Wireless Headphones",
description="Premium noise-canceling wireless headphones",
price=199.99,
category_id="category_electronics",
sku="WH-2024-BLK",
track_inventory=True,
stock_quantity=75,
is_active=True,
)
)
Get, update, delete
- TypeScript
- Python
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');
from wiil.models.business_mgt import UpdateBusinessProduct
from wiil.types import PaginationRequest
by_id = client.products.get("product_123")
by_sku = client.products.get_by_sku("WH-2024-BLK")
results = client.products.list(params=PaginationRequest(page=1, page_size=50), include_deleted=False)
updated = client.products.update(
UpdateBusinessProduct(id="product_123", price=179.99, stock_quantity=100)
)
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.
- TypeScript
- Python
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 }],
},
]);
from wiil.models.business_mgt import CreateBusinessProduct
products = client.products.create_batch([
CreateBusinessProduct(name="Wireless Earbuds", description="Bluetooth 5.0 earbuds", price=79.99, sku="ELEC-001", category_id="cat_electronics", is_active=True),
CreateBusinessProduct(name="Cotton T-Shirt", description="100% cotton", price=24.99, sku="CLTH-001", category_id="cat_clothing", is_active=True),
CreateBusinessProduct(name="Leather Wallet", description="Genuine leather bifold", price=49.99, sku="ACCS-001", category_id="cat_accessories", is_active=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.
- TypeScript
- Python
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 },
});
from time import time
from wiil.models.business_mgt import CreateProductOrder, OrderAddress, OrderPricing, ProductOrderItemBase
order = client.product_orders.create(
CreateProductOrder(
items=[
ProductOrderItemBase(
product_id="prod_123",
item_name="Wireless Headphones",
sku="WH-2024-BLK",
quantity=2,
unit_price=79.99,
total_price=159.98,
)
],
customer_id="cust_789",
pricing=OrderPricing(subtotal=159.98, tax=14.40, shipping_amount=9.99, total=184.37),
order_date=int(time() * 1000),
shipping_method="Standard",
source="web",
)
)
Read orders
- TypeScript
- Python
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}`));
from wiil.types import PaginationRequest
order_details = client.product_orders.get("order_123")
customer_orders = client.product_orders.get_by_customer(
"cust_789", PaginationRequest(page=1, page_size=20)
)
print(order_details.status, customer_orders.meta.total_count)
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
isAlcoholicto trigger age verification in the order flow. - Follow the status lifecycle —
pending → confirmed → preparing → ready → out_for_delivery → completed; record acancelReasonwhen cancelling.
Next steps
- Catalog reference: Categories, Products, Variants & axes, Orders
- Notifications: order updates via outbound communications