Categories & Items
A menu is built from three layers. A category groups dishes for display ("Appetizers", "Pizzas"). A menu item is a single dish with its description, ingredients, allergens, and nutrition. A variant is a priced version of that dish ("Small", "Large") — and every item must have at least one, because price lives on the variant, not the item.
Menu categories
Categories organize items into logical groups for navigation and display.
Create a category
- TypeScript
- Python
const category = await client.menus.createCategory({
name: 'Appetizers',
description: 'Start your meal right',
displayOrder: 1,
});
from wiil.models.business_mgt import CreateMenuCategory
category = client.menus.create_category(
CreateMenuCategory(name="Appetizers", description="Start your meal right", display_order=1)
)
Get, list, update, delete
- TypeScript
- Python
const loaded = await client.menus.getCategory('cat_123');
const result = await client.menus.listCategories();
const updated = await client.menus.updateCategory({
id: 'cat_123',
name: 'Premium Appetizers',
displayOrder: 2,
});
await client.menus.deleteCategory('cat_123');
from wiil.models.business_mgt import UpdateMenuCategory
loaded = client.menus.get_category("cat_123")
categories = client.menus.list_categories()
updated = client.menus.update_category(
UpdateMenuCategory(id="cat_123", name="Premium Appetizers", display_order=2)
)
client.menus.delete_category("cat_123")
Batch create
- TypeScript
- Python
const result = await client.menus.createCategoryBatch([
{ name: 'Appetizers', description: 'Start your meal', displayOrder: 1 },
{ name: 'Main Courses', description: 'Signature entrees', displayOrder: 2 },
{ name: 'Desserts', description: 'Sweet treats', displayOrder: 3 },
]);
from wiil.models.business_mgt import CreateMenuCategory
categories = client.menus.create_category_batch([
CreateMenuCategory(name="Appetizers", display_order=1),
CreateMenuCategory(name="Main Courses", display_order=2),
CreateMenuCategory(name="Desserts", display_order=3),
])
Both SDKs cap batch creation at 50 categories per request.
MenuCategory fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | auto | Unique identifier |
name | string | Yes | Category name (min 1 character) |
description | string | null | No | Context for the category |
displayOrder | integer | null | No | Display order (lower = first) |
channelMappings | array | null | No | Per-channel external category IDs (channelId, externalCategoryId) |
createdAt / updatedAt | number | auto | Timestamps |
Menu items
A menu item describes a dish. In the TypeScript SDK an item is created together with its required variants; the response is the hydrated item with its variants, resolved priceRange, and modifierGroups. In the Python SDK, create_item sets the item's base price directly.
Create an item
- TypeScript
- Python
const pizza = await client.menus.createItem({
categoryId: 'cat_main',
name: 'Margherita Pizza',
description: 'Classic tomato and mozzarella',
price: 14.99,
isAvailable: true,
isActive: true,
preparationTime: 20,
displayOrder: 1,
variants: [
{ name: 'Small (10")', price: 14.99, isDefault: true, isActive: true, isAvailable: true },
{ name: 'Medium (12")', price: 18.99, isDefault: false, isActive: true, isAvailable: true },
{ name: 'Large (14")', price: 22.99, isDefault: false, isActive: true, isAvailable: true },
],
});
console.log(`Created ${pizza.name} with ${pizza.variants.length} sizes`);
from wiil.models.business_mgt import CreateBusinessMenuItem
item = client.menus.create_item(
CreateBusinessMenuItem(
name="Margherita Pizza",
description="Classic tomato and mozzarella",
price=14.99,
category_id="cat_main",
ingredients=["flour", "tomatoes", "mozzarella", "basil"],
allergens=["gluten", "dairy"],
is_available=True,
preparation_time=20,
is_active=True,
)
)
Get, list, update, delete
- TypeScript
- Python
const item = await client.menus.getItem('item_123');
const result = await client.menus.listItems();
const updated = await client.menus.updateItem({
id: 'item_123',
description: 'Fresh romaine with premium Caesar dressing',
isAvailable: true,
isActive: true,
});
await client.menus.deleteItem('item_123');
from wiil.models.business_mgt import UpdateBusinessMenuItem
from wiil.types import PaginationRequest
item = client.menus.get_item("item_123")
all_items = client.menus.list_items(PaginationRequest(page=1, page_size=50), include_deleted=False)
by_category = client.menus.get_items_by_category("cat_main", include_unavailable=False)
popular = client.menus.get_popular_items(limit=10)
updated = client.menus.update_item(
UpdateBusinessMenuItem(id="item_123", price=10.99, is_available=True)
)
client.menus.delete_item("item_123")
Batch create
- TypeScript
- Python
const result = await client.menus.createItemBatch([
{
categoryId: 'cat_appetizers',
name: 'Buffalo Wings',
description: 'Crispy wings with hot sauce',
price: 11.99,
isAvailable: true,
isActive: true,
variants: [{ name: 'Default', price: 11.99, isDefault: true, isActive: true, isAvailable: true }],
},
{
categoryId: 'cat_appetizers',
name: 'Mozzarella Sticks',
description: 'Breaded and fried with marinara',
price: 8.99,
isAvailable: true,
isActive: true,
variants: [{ name: 'Default', price: 8.99, isDefault: true, isActive: true, isAvailable: true }],
},
]);
from wiil.models.business_mgt import CreateBusinessMenuItem
items = client.menus.create_item_batch([
CreateBusinessMenuItem(name="Caesar Salad", description="Fresh romaine", price=12.00, category_id="cat_appetizers", is_available=True),
CreateBusinessMenuItem(name="Grilled Salmon", description="Atlantic salmon", price=28.00, category_id="cat_main", is_available=True),
])
Both SDKs cap batch creation at 100 items per request.
BusinessMenuItem fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | auto | Unique identifier |
name | string | Yes | Item name (min 1 character) |
description | string | null | No | Detailed description |
price | number | Yes | Base price (≥ 0) |
categoryId | string | Yes | Parent category (nullable on create) |
ingredients | string[] | null | No | Primary ingredients |
allergens | string[] | null | No | Allergen warnings (e.g. nuts, dairy) |
nutritionalInfo | object | null | No | calories, protein, carbs, fat |
isAvailable | boolean | No | Real-time availability (default true) |
preparationTime | integer | null | No | Estimated prep time (minutes, > 0) |
isActive | boolean | No | Whether item appears in the menu (default true) |
displayOrder | integer | null | No | Order within category |
dayParts | array | null | No | Recurring availability windows (see below) |
channelMappings | array | null | No | Per-channel external item IDs |
orderCount / recentOrderCount / lastOrderedAt | — | read-only | Server-computed analytics |
Daypart
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Daypart name (e.g. "Breakfast") |
startTime / endTime | string | Yes | HH:MM window |
daysOfWeek | array | No | Active days (0 = Sunday, 6 = Saturday) |
Every menu item must have at least one variant — price lives on the variant. The TypeScript SDK creates variants inline with the item (and exposes a dedicated variants resource). The Python create_item sets the item's base price; the platform represents that as the item's variant pricing.
Menu item variants
A variant is a size/option of an item with its own price. The TypeScript SDK manages variants as a dedicated resource for fine-grained control (add a size, change one price, mark a default). The Python menu guide prices items through the item's price and does not expose a separate variant client.
const variant = await client.menuItemVariants.create({
menuItemId: 'item_123',
name: 'Extra Large',
sku: 'PIZZA-XL-001',
price: 26.99,
isDefault: false,
displayOrder: 4,
});
const loaded = await client.menuItemVariants.get('variant_123');
const defaultVariant = await client.menuItemVariants.getDefault('item_123');
const updated = await client.menuItemVariants.update('variant_123', {
id: 'variant_123',
price: 27.99,
isAvailable: true,
});
const batch = await client.menuItemVariants.createBatch([
{ menuItemId: 'item_123', name: 'Medium', price: 18.99, isDefault: false, isActive: true, isAvailable: true },
{ menuItemId: 'item_123', name: 'Large', price: 22.99, isDefault: false, isActive: true, isAvailable: true },
]);
await client.menuItemVariants.delete('variant_123');
MenuItemVariant fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | auto | Unique identifier |
menuItemId | string | Yes | Parent menu item |
name | string | Yes | Variant name (e.g. "Large") |
description | string | null | No | Variant description |
price | number | Yes | Variant price (≥ 0) |
isAvailable | boolean | No | Available for ordering (default true) |
isActive | boolean | No | Appears in the menu (default true) |
isDefault | boolean | No | Pre-selected default (default false) |
variantChannelMappings | array | null | No | Per-channel external variant IDs |
Give one variant isDefault: true for predictable UI, and use descriptive names that include size detail (e.g. Small (10") rather than just Small).
Query options
| Resource | Filters | Sort fields |
|---|---|---|
BusinessMenuItem | search, locationId, categoryId, isActive, isAvailable, allergens | name, price, createdAt, displayOrder |
MenuItemVariant | search, menuItemId, isActive, isAvailable, priceRange | name, price, createdAt |
All sorts take a direction of asc or desc, with page/pageSize pagination.