Menu Management Guide
This guide covers managing restaurant menus and food service orders using the WIIL Platform JS SDK.
Quick Start​
- TypeScript
- Python
import { WiilClient, MenuOrderType, OrderStatus, PaymentStatus } from 'wiil-js';
const client = new WiilClient({
apiKey: 'your-api-key',
});
// Create a menu category
const category = await client.menus.createCategory({
name: 'Main Courses',
description: 'Signature entrees and main dishes',
displayOrder: 1,
});
// Create a menu item
const menuItem = await client.menus.createItem({
name: 'Cheeseburger',
description: 'Angus beef with aged cheddar',
price: 12.99,
categoryId: category.id,
ingredients: ['beef', 'cheese', 'lettuce', 'tomato', 'bun'],
allergens: ['gluten', 'dairy'],
nutritionalInfo: {
calories: 650,
protein: 35,
carbs: 48,
fat: 32,
},
isAvailable: true,
preparationTime: 15,
isActive: true,
displayOrder: 1,
});
// Create an order
const order = await client.menuOrders.create({
type: MenuOrderType.TAKEOUT,
items: [
{
menuItemId: menuItem.id,
itemName: menuItem.name,
quantity: 2,
unitPrice: menuItem.price,
totalPrice: menuItem.price * 2,
},
],
customerId: 'cust_123',
pricing: {
subtotal: 25.98,
tax: 2.60,
tip: 5.00,
total: 33.58,
},
orderDate: Date.now(),
source: 'web',
});
from time import time
from wiil import WiilClient
from wiil.models.business_mgt import (
CreateBusinessMenuItem,
CreateMenuCategory,
CreateMenuOrder,
MenuOrderItemBase,
OrderPricing,
)
client = WiilClient(api_key="your-api-key")
now_ms = int(time() * 1000)
category = client.menus.create_category(
CreateMenuCategory(name="Main Courses", description="Signature entrees", display_order=1)
)
menu_item = client.menus.create_item(
CreateBusinessMenuItem(
name="Cheeseburger",
description="Angus beef with aged cheddar",
price=12.99,
category_id=category.id,
ingredients=["beef", "cheese", "lettuce", "tomato", "bun"],
allergens=["gluten", "dairy"],
is_available=True,
preparation_time=15,
is_active=True,
)
)
order = client.menu_orders.create(
CreateMenuOrder(
type="takeout",
items=[
MenuOrderItemBase(
menu_item_id=menu_item.id,
item_name=menu_item.name,
quantity=2,
unit_price=menu_item.price,
total_price=menu_item.price * 2,
)
],
customer_id="cust_123",
pricing=OrderPricing(subtotal=25.98, tax=2.60, tip=5.00, total=33.58, currency="USD"),
order_date=now_ms,
source="web",
)
)
print("Created menu item:", menu_item.id)
print("Created menu order:", order.id)
Menu Categories​
Category Schema​
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Category name |
| description | string | No | Category description |
| displayOrder | number | No | Display order for sorting |
Create Category​
- TypeScript
- Python
const category = await client.menus.createCategory({
name: 'Appetizers',
description: 'Start your meal right',
displayOrder: 1,
});
console.log('Category created:', category.id);
from wiil.models.business_mgt import CreateMenuCategory, UpdateMenuCategory
category = client.menus.create_category(
CreateMenuCategory(name="Appetizers", description="Start your meal right", display_order=1)
)
loaded = client.menus.get_category(category.id)
categories = client.menus.list_categories()
updated = client.menus.update_category(
UpdateMenuCategory(id=category.id, name="Premium Appetizers", display_order=2)
)
deleted = client.menus.delete_category(updated.id)
print(loaded.name, len(categories), deleted)
Get Category​
const category = await client.menus.getCategory('cat_123');
console.log('Category:', category.name);
List Categories​
const result = await client.menus.listCategories({
page: 1,
pageSize: 20,
});
console.log('Categories:', result.data.length);
console.log('Total:', result.meta.totalCount);
Update Category​
const updated = await client.menus.updateCategory({
id: 'cat_123',
name: 'Premium Appetizers',
displayOrder: 2,
});
console.log('Category updated:', updated.name);
Delete Category​
const deleted = await client.menus.deleteCategory('cat_123');
console.log('Deleted:', deleted);
Menu Items​
Item Schema​
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Item name |
| description | string | No | Item description |
| price | number | Yes | Item price |
| categoryId | string | No | Category ID (nullable) |
| ingredients | string[] | No | List of ingredients |
| allergens | string[] | No | Allergen information |
| nutritionalInfo | object | No | Nutritional data (calories, protein, carbs, fat) |
| isAvailable | boolean | No | Available for ordering (default: true) |
| preparationTime | number | No | Preparation time in minutes |
| isActive | boolean | No | Item is active (default: true) |
| displayOrder | number | No | Display order for sorting |
Create Item​
- TypeScript
- Python
const item = await client.menus.createItem({
name: 'Caesar Salad',
description: 'Fresh romaine with house-made dressing',
price: 9.99,
categoryId: 'cat_123',
ingredients: ['romaine lettuce', 'parmesan', 'croutons', 'caesar dressing'],
allergens: ['dairy', 'gluten', 'eggs'],
nutritionalInfo: {
calories: 320,
protein: 8,
carbs: 22,
fat: 18,
},
isAvailable: true,
preparationTime: 10,
isActive: true,
displayOrder: 1,
});
console.log('Item created:', item.id);
from wiil.models.business_mgt import CreateBusinessMenuItem, UpdateBusinessMenuItem
from wiil.types import PaginationRequest
item = client.menus.create_item(
CreateBusinessMenuItem(
name="Caesar Salad",
description="Fresh romaine with house-made dressing",
price=9.99,
category_id="cat_123",
preparation_time=10,
is_available=True,
)
)
fetched = client.menus.get_item(item.id)
all_items = client.menus.list_items(PaginationRequest(page=1, page_size=50), include_deleted=False)
by_category = client.menus.get_items_by_category("cat_123", include_unavailable=False)
popular = client.menus.get_popular_items(limit=10)
updated = client.menus.update_item(
UpdateBusinessMenuItem(id=item.id, price=10.99, is_available=True)
)
deleted = client.menus.delete_item(updated.id)
print(fetched.name, len(all_items.data), len(by_category), len(popular), deleted)
Get Item​
const item = await client.menus.getItem('item_123');
console.log('Item:', item.name);
console.log('Price:', item.price);
List All Items​
const result = await client.menus.listItems({
page: 1,
pageSize: 50,
includeDeleted: false,
});
console.log('Items:', result.data.length);
Get Items by Category​
const result = await client.menus.getItemsByCategory('cat_123', {
page: 1,
pageSize: 20,
includeUnavailable: false,
});
console.log('Items in category:', result.data.length);
Get Popular Items​
const result = await client.menus.getPopularItems({
page: 1,
pageSize: 10,
});
console.log('Top items:', result.data.map(item => item.name));
Update Item​
const updated = await client.menus.updateItem({
id: 'item_123',
price: 10.99,
isAvailable: true,
});
console.log('Item updated:', updated.name);
Delete Item​
const deleted = await client.menus.deleteItem('item_123');
console.log('Deleted:', deleted);
Batch Operations​
Batch operations allow you to create multiple resources in a single API request, improving performance for bulk data imports.
Batch Create Categories​
Create up to 50 menu categories in a single request:
- TypeScript
- Python
const categories = 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 },
{ name: 'Beverages', description: 'Drinks and refreshments', displayOrder: 4 },
]);
console.log(`Created ${categories.data.length} categories`);
categories.data.forEach(cat => {
console.log(`- ${cat.name}: ${cat.id}`);
});
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),
CreateMenuCategory(name="Beverages", display_order=4),
])
print(f"Created {len(categories.data)} categories")
for category in categories.data:
print(f" - {category.name}")
Batch Create Menu Items​
Create up to 100 menu items in a single request:
- TypeScript
- Python
const items = await client.menus.createItemBatch([
{
name: 'Buffalo Wings',
description: 'Crispy wings with hot sauce',
price: 11.99,
categoryId: 'cat_appetizers',
ingredients: ['chicken wings', 'buffalo sauce'],
allergens: ['dairy'],
isAvailable: true,
preparationTime: 20,
isActive: true,
},
{
name: 'Caesar Salad',
description: 'Fresh romaine with house dressing',
price: 9.99,
categoryId: 'cat_appetizers',
ingredients: ['romaine', 'parmesan', 'croutons'],
allergens: ['dairy', 'gluten'],
isAvailable: true,
preparationTime: 10,
isActive: true,
},
{
name: 'Classic Burger',
description: 'Angus beef with all the fixings',
price: 14.99,
categoryId: 'cat_main',
ingredients: ['beef', 'lettuce', 'tomato', 'onion'],
allergens: ['gluten'],
isAvailable: true,
preparationTime: 15,
isActive: true,
},
]);
console.log(`Created ${items.data.length} menu items`);
from wiil.models.business_mgt import CreateBusinessMenuItem
items = client.menus.create_item_batch([
CreateBusinessMenuItem(
name="Caesar Salad",
description="Fresh romaine with caesar dressing",
price=12.00,
category_id="cat_appetizers",
is_available=True,
),
CreateBusinessMenuItem(
name="Grilled Salmon",
description="Atlantic salmon with vegetables",
price=28.00,
category_id="cat_main",
is_available=True,
),
CreateBusinessMenuItem(
name="Chocolate Cake",
description="Rich chocolate layer cake",
price=9.00,
category_id="cat_desserts",
is_available=True,
),
])
print(f"Created {len(items.data)} menu items")
for item in items.data:
print(f" - {item.name}: ${item.price}")
Batch Limits​
| Resource | Maximum per Batch |
|---|---|
| Categories | 50 |
| Menu Items | 100 |
Note: Batch operations validate each item individually. If validation fails for any item, the entire batch request fails with an error indicating the index of the failing item.
try {
const items = await client.menus.createItemBatch(menuItems);
} catch (error) {
// Error message includes the index: "Validation failed for item at index 2"
console.error('Batch creation failed:', error.message);
}
Menu Orders​
Order Schema​
| Field | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Order type: 'dine_in', 'takeout', 'delivery' |
| items | array | Yes | Order items (see Item Schema below) |
| customerId | string | Yes | Customer ID |
| customer | object | No | Customer information (name, phone, email, address) |
| pricing | object | Yes | Pricing breakdown (subtotal, tax, tip, shippingAmount, discount, total, currency) |
| orderDate | number | Yes | Order date (Unix timestamp) |
| paymentStatus | string | No | Payment status (default: 'pending') |
| paymentMethod | string | No | Payment method |
| paymentReference | string | No | Payment reference number |
| requestedTime | number | No | Requested pickup/delivery time |
| estimatedReadyTime | number | No | Estimated ready time |
| specialInstructions | string | No | Special instructions |
| allergies | string[] | No | Customer allergies |
| tableNumber | string | No | Table number (for dine-in) |
| externalOrderId | string | No | External order ID |
| source | string | No | Order source (default: 'direct') |
| deliveryAddress | object | No | Delivery address (street, city, postalCode) |
Order Item Schema​
| Field | Type | Required | Description |
|---|---|---|---|
| menuItemId | string | Yes | Menu item ID |
| itemName | string | Yes | Item name |
| quantity | number | Yes | Quantity ordered |
| unitPrice | number | Yes | Price per unit |
| totalPrice | number | Yes | Total price for this item |
| specialInstructions | string | No | Special preparation instructions |
| customizations | array | No | Customizations (name, value, additionalCost) |
| preparationTime | number | No | Preparation time in minutes |
| notes | string | No | Additional notes |
Note: Status defaults to 'pending' and should not be included in create requests.
Create Order​
- TypeScript
- Python
const order = await client.menuOrders.create({
type: MenuOrderType.TAKEOUT,
items: [
{
menuItemId: 'item_123',
itemName: 'Cheeseburger',
quantity: 2,
unitPrice: 12.99,
totalPrice: 25.98,
specialInstructions: 'No onions',
customizations: [
{
name: 'Extra Cheese',
value: 'Yes',
additionalCost: 1.50,
},
],
preparationTime: 15,
},
],
customerId: 'cust_456',
customer: {
name: 'John Doe',
phone: '+12125551234',
email: 'john@example.com',
},
pricing: {
subtotal: 25.98,
tax: 2.60,
tip: 5.00,
total: 33.58,
currency: 'USD',
},
orderDate: Date.now(),
requestedTime: Date.now() + 3600000,
specialInstructions: 'Call upon arrival',
source: 'web',
});
console.log('Order created:', order.id);
console.log('Order number:', order.orderNumber);
from wiil.models.business_mgt import CreateMenuOrder, MenuOrderItemBase, OrderPricing
from wiil.types import PaginationRequest
order = client.menu_orders.create(
CreateMenuOrder(
type="delivery",
items=[
MenuOrderItemBase(
menu_item_id="item_123",
item_name="Cheeseburger",
quantity=2,
unit_price=12.99,
total_price=25.98,
special_instructions="No onions",
)
],
customer_id="cust_456",
pricing=OrderPricing(subtotal=25.98, tax=2.60, tip=5.00, total=33.58),
order_date=int(time() * 1000),
source="web",
)
)
loaded = client.menu_orders.get(order.id)
customer_orders = client.menu_orders.get_by_customer(
"cust_456", PaginationRequest(page=1, page_size=20)
)
updated_status = client.menu_orders.update_status(order.id, "confirmed")
cancelled = client.menu_orders.cancel(order.id, reason="Customer requested cancellation")
print(loaded.id, customer_orders.meta.total_count, updated_status.status, cancelled.status)
Create Delivery Order​
const order = await client.menuOrders.create({
type: MenuOrderType.DELIVERY,
items: [
{
menuItemId: 'item_456',
itemName: 'Pizza',
quantity: 1,
unitPrice: 18.99,
totalPrice: 18.99,
},
],
customerId: 'cust_789',
pricing: {
subtotal: 18.99,
tax: 1.90,
shippingAmount: 5.00,
total: 25.89,
},
orderDate: Date.now(),
source: 'direct',
deliveryAddress: {
street: '123 Main St',
city: 'New York',
postalCode: '10001',
},
});
console.log('Delivery order created:', order.id);
Get Order​
const order = await client.menuOrders.get('order_123');
console.log('Order:', order.orderNumber);
console.log('Status:', order.status);
console.log('Items:', order.items.length);
Get Orders by Customer​
const result = await client.menuOrders.getByCustomer('cust_123', {
page: 1,
pageSize: 20,
});
console.log('Customer orders:', result.data.length);
Update Order​
const updated = await client.menuOrders.update({
id: 'order_123',
status: OrderStatus.READY,
estimatedReadyTime: Date.now() + 600000,
});
console.log('Order updated:', updated.status);
Update Order Status​
const updated = await client.menuOrders.updateStatus('order_123', {
status: OrderStatus.PREPARING,
});
console.log('Status updated:', updated.status);
Cancel Order​
const cancelled = await client.menuOrders.cancel('order_123', {
reason: 'Customer requested cancellation',
});
console.log('Order cancelled:', cancelled.status);
console.log('Reason:', cancelled.cancelReason);
Delete Order​
const deleted = await client.menuOrders.delete('order_123');
console.log('Deleted:', deleted);
List Orders​
const result = await client.menuOrders.list({
page: 1,
pageSize: 20,
});
console.log('Orders:', result.data.length);
console.log('Total:', result.meta.totalCount);
Order Status Values​
Order status follows this enum:
enum OrderStatus {
PENDING = 'pending',
CONFIRMED = 'confirmed',
PREPARING = 'preparing',
READY = 'ready',
OUT_FOR_DELIVERY = 'out_for_delivery',
COMPLETED = 'completed',
CANCELLED = 'cancelled',
RETURNED = 'returned'
}
Payment Status Values​
Payment status follows this enum:
enum PaymentStatus {
PENDING = 'pending',
PAID = 'paid',
PARTIAL = 'partial',
FAILED = 'failed',
REFUNDED = 'refunded'
}
Order Type Values​
Order type follows this enum:
enum MenuOrderType {
DINE_IN = 'dine_in',
TAKEOUT = 'takeout',
DELIVERY = 'delivery'
}
Complete Example: Restaurant Menu Setup​
import { WiilClient, MenuOrderType, OrderStatus, PaymentStatus } from 'wiil-js';
async function setupRestaurantMenu() {
const client = new WiilClient({
apiKey: process.env.WIIL_API_KEY!,
});
// 1. Create menu categories
const appetizers = await client.menus.createCategory({
name: 'Appetizers',
description: 'Start your meal right',
displayOrder: 1,
});
const entrees = await client.menus.createCategory({
name: 'Main Courses',
description: 'Signature entrees',
displayOrder: 2,
});
// 2. Create menu items
const wings = await client.menus.createItem({
name: 'Buffalo Wings',
description: 'Crispy chicken wings with hot sauce',
price: 11.99,
categoryId: appetizers.id,
ingredients: ['chicken wings', 'buffalo sauce', 'celery', 'ranch dressing'],
allergens: ['dairy'],
nutritionalInfo: {
calories: 540,
protein: 42,
carbs: 18,
fat: 32,
},
isAvailable: true,
preparationTime: 20,
isActive: true,
displayOrder: 1,
});
const burger = await client.menus.createItem({
name: 'Classic Burger',
description: 'Angus beef with all the fixings',
price: 14.99,
categoryId: entrees.id,
ingredients: ['beef', 'lettuce', 'tomato', 'onion', 'bun'],
allergens: ['gluten'],
nutritionalInfo: {
calories: 680,
protein: 38,
carbs: 52,
fat: 34,
},
isAvailable: true,
preparationTime: 15,
isActive: true,
displayOrder: 1,
});
// 3. Process a customer order
const order = await client.menuOrders.create({
type: MenuOrderType.DINE_IN,
items: [
{
menuItemId: wings.id,
itemName: wings.name,
quantity: 1,
unitPrice: wings.price,
totalPrice: wings.price,
},
{
menuItemId: burger.id,
itemName: burger.name,
quantity: 2,
unitPrice: burger.price,
totalPrice: burger.price * 2,
specialInstructions: 'Medium rare',
},
],
customerId: 'cust_123',
customer: {
name: 'Sarah Johnson',
phone: '+12125555678',
email: 'sarah@example.com',
},
pricing: {
subtotal: 41.97,
tax: 4.20,
tip: 8.00,
total: 54.17,
currency: 'USD',
},
orderDate: Date.now(),
tableNumber: 'T-12',
source: 'direct',
});
console.log('Order created:', order.orderNumber);
// 4. Update order status as it progresses
await client.menuOrders.updateStatus(order.id, {
status: OrderStatus.CONFIRMED,
});
await client.menuOrders.updateStatus(order.id, {
status: OrderStatus.PREPARING,
});
await client.menuOrders.update({
id: order.id,
status: OrderStatus.READY,
estimatedReadyTime: Date.now() + 300000,
});
// 5. Complete the order
await client.menuOrders.updateStatus(order.id, {
status: OrderStatus.COMPLETED,
});
// 6. Get customer's order history
const customerOrders = await client.menuOrders.getByCustomer('cust_123');
console.log('Customer has', customerOrders.data.length, 'orders');
}
Best Practices​
Menu Organization​
- Use categories to organize your menu logically
- Set displayOrder for both categories and items to control presentation
- Keep descriptions clear and concise
Item Management​
- Always include allergen information for food safety
- Provide accurate nutritional information when possible
- Use preparationTime to help estimate order completion
- Mark items as unavailable rather than deleting when out of stock
Order Processing​
- Always validate item availability before creating an order
- Calculate pricing accurately including tax and fees
- Include customer contact information for order updates
- Use status updates to track order progress
Pricing Calculations​
const items = [
{ price: 12.99, quantity: 2 }, // 25.98
{ price: 8.99, quantity: 1 }, // 8.99
];
const subtotal = items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const tax = subtotal * 0.0875; // 8.75% tax rate
const tip = subtotal * 0.20; // 20% tip
const total = subtotal + tax + tip;
const pricing = {
subtotal: Number(subtotal.toFixed(2)),
tax: Number(tax.toFixed(2)),
tip: Number(tip.toFixed(2)),
shippingAmount: 0,
discount: 0,
total: Number(total.toFixed(2)),
currency: 'USD',
};
Status Management​
Follow this typical order lifecycle:
- Order created → 'pending'
- Restaurant confirms → 'confirmed'
- Kitchen starts → 'preparing'
- Food ready → 'ready'
- For delivery → 'out_for_delivery'
- Complete → 'completed'
Troubleshooting​
Problem: Missing required customer ID​
Error:
ValidationError: customerId is required
Solution: Ensure you provide a valid customer ID:
const order = await client.menuOrders.create({
customerId: 'cust_123', // Required
// ... other fields
});
Problem: Invalid pricing calculation​
Error:
ValidationError: pricing.total does not match sum of components
Solution: Verify your pricing calculation:
const pricing = {
subtotal: 25.98,
tax: 2.60,
tip: 5.00,
shippingAmount: 0,
discount: 0,
total: 33.58, // Must equal subtotal + tax + tip + shippingAmount - discount
currency: 'USD',
};
Problem: Item not found when creating order​
Error:
NotFoundError: Menu item not found
Solution: Verify the menu item exists and is active:
const item = await client.menus.getItem('item_123');
if (!item.isActive || !item.isAvailable) {
throw new Error('Item is not available for ordering');
}
Problem: Invalid order type​
Error:
ValidationError: Invalid order type
Solution: Use the MenuOrderType enum values:
import { MenuOrderType } from 'wiil-js';
const order = await client.menuOrders.create({
type: MenuOrderType.DINE_IN, // or TAKEOUT, DELIVERY
// ... other fields
});