Skip to main content

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.

Category, item, and variant structureA category groups menu items one-to-many. Each item requires at least one variant, which carries the price. Categories, items, and variants each support channel mappings for delivery-platform sync.1 : N1 : N (min 1)CategorygroupingMenu itemdish · ingredients · allergensVariantcarries the priceChannel mappingsevery level maps to external IDs for DoorDash, Uber Eats, and other delivery platforms

Categories organize items into logical groups for navigation and display.

Create a category

const category = await client.menus.createCategory({
name: 'Appetizers',
description: 'Start your meal right',
displayOrder: 1,
});

Get, list, update, delete

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');

Batch create

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 },
]);
Limits

Both SDKs cap batch creation at 50 categories per request.

FieldTypeRequiredDescription
idstringautoUnique identifier
namestringYesCategory name (min 1 character)
descriptionstring | nullNoContext for the category
displayOrderinteger | nullNoDisplay order (lower = first)
channelMappingsarray | nullNoPer-channel external category IDs (channelId, externalCategoryId)
createdAt / updatedAtnumberautoTimestamps

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

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`);

Get, list, update, delete

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');

Batch create

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 }],
},
]);
Limits

Both SDKs cap batch creation at 100 items per request.

BusinessMenuItem fields

FieldTypeRequiredDescription
idstringautoUnique identifier
namestringYesItem name (min 1 character)
descriptionstring | nullNoDetailed description
pricenumberYesBase price (≥ 0)
categoryIdstringYesParent category (nullable on create)
ingredientsstring[] | nullNoPrimary ingredients
allergensstring[] | nullNoAllergen warnings (e.g. nuts, dairy)
nutritionalInfoobject | nullNocalories, protein, carbs, fat
isAvailablebooleanNoReal-time availability (default true)
preparationTimeinteger | nullNoEstimated prep time (minutes, > 0)
isActivebooleanNoWhether item appears in the menu (default true)
displayOrderinteger | nullNoOrder within category
dayPartsarray | nullNoRecurring availability windows (see below)
channelMappingsarray | nullNoPer-channel external item IDs
orderCount / recentOrderCount / lastOrderedAtread-onlyServer-computed analytics

Daypart

FieldTypeRequiredDescription
namestringYesDaypart name (e.g. "Breakfast")
startTime / endTimestringYesHH:MM window
daysOfWeekarrayNoActive days (0 = Sunday, 6 = Saturday)
Variants are required

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.

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');
FieldTypeRequiredDescription
idstringautoUnique identifier
menuItemIdstringYesParent menu item
namestringYesVariant name (e.g. "Large")
descriptionstring | nullNoVariant description
pricenumberYesVariant price (≥ 0)
isAvailablebooleanNoAvailable for ordering (default true)
isActivebooleanNoAppears in the menu (default true)
isDefaultbooleanNoPre-selected default (default false)
variantChannelMappingsarray | nullNoPer-channel external variant IDs
tip

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

ResourceFiltersSort fields
BusinessMenuItemsearch, locationId, categoryId, isActive, isAvailable, allergensname, price, createdAt, displayOrder
MenuItemVariantsearch, menuItemId, isActive, isAvailable, priceRangename, price, createdAt

All sorts take a direction of asc or desc, with page/pageSize pagination.