Skip to content
Maple

Menus

Worked menu fragments for the scenarios real restaurants need — multi-select modifiers, nested choices, dayparted specials, and happy-hour discounts.

These are copy-and-adapt fragments for common menu scenarios. Each shows only the relevant part of the menu document — drop it into the full structure from Publish a menu. For the model behind them, see The menu model.

All prices are integer minor units (450 = $4.50), and every object carries an externalId you own.

Multi-select: choose up to 3, max 2 different

“Pick your toppings — up to 3 total, but no more than 2 different kinds.” The total count and the distinct count are separate rules:

{
"externalId": "grp-toppings",
"name": "Toppings",
"minSelections": 0,
"maxSelections": 3,
"maxUniqueSelections": 2,
"options": [
{ "externalId": "top-mushroom", "name": "Mushroom", "maxQuantity": 3, "price": { "amount": { "amountMinor": 150, "currency": "USD" } } },
{ "externalId": "top-pepperoni", "name": "Pepperoni", "maxQuantity": 3, "price": { "amount": { "amountMinor": 200, "currency": "USD" } } },
{ "externalId": "top-olives", "name": "Olives", "maxQuantity": 3, "price": { "amount": { "amountMinor": 100, "currency": "USD" } } }
]
}
  • maxSelections: 3 caps the total quantity selected.
  • maxUniqueSelections: 2 caps the number of distinct options. A guest can take 2× Mushroom + 1× Olives (3 total, 2 kinds), but not three different toppings.
  • maxQuantity: 3 on each option allows doubling or tripling a single topping.

Nested modifiers

A choice that opens further choices — pick a side, and if it’s the salad, pick a dressing. Options can carry their own modifier groups:

{
"externalId": "itm-combo",
"name": "Burger Combo",
"variations": [{ "externalId": "var-combo", "name": "Regular", "price": { "amount": { "amountMinor": 1200, "currency": "USD" } } }],
"modifierGroups": [
{
"externalId": "grp-side",
"name": "Choose your side",
"minSelections": 1,
"maxSelections": 1,
"options": [
{ "externalId": "side-fries", "name": "Fries" },
{
"externalId": "side-salad",
"name": "Side salad",
"modifierGroups": [
{
"externalId": "grp-dressing",
"name": "Dressing",
"minSelections": 1,
"maxSelections": 1,
"options": [
{ "externalId": "drs-ranch", "name": "Ranch" },
{ "externalId": "drs-vinaigrette", "name": "Vinaigrette" }
]
}
]
}
]
}
]
}

Choosing Side salad reveals a required Dressing choice; choosing Fries doesn’t. Nest as deep as the menu needs.

Availability: daily specials

A breakfast section that only shows on weekday mornings. Put the window on the category (it narrows everything inside) or on a single item:

{
"externalId": "cat-breakfast",
"name": "Breakfast",
"availability": {
"windows": [
{ "dayOfWeek": 1, "startTime": "07:00", "endTime": "11:00" },
{ "dayOfWeek": 2, "startTime": "07:00", "endTime": "11:00" },
{ "dayOfWeek": 3, "startTime": "07:00", "endTime": "11:00" },
{ "dayOfWeek": 4, "startTime": "07:00", "endTime": "11:00" },
{ "dayOfWeek": 5, "startTime": "07:00", "endTime": "11:00" }
]
},
"items": [{ "externalId": "itm-pancakes", "name": "Pancakes", "variations": [{ "externalId": "var-pancakes", "name": "Regular", "price": { "amount": { "amountMinor": 950, "currency": "USD" } } }] }]
}
  • dayOfWeek is 0 (Sunday) through 6 (Saturday); add one window per active day.
  • startTime / endTime are local HH:MM (24-hour), in the menu’s timezone — set the top-level timezone (it defaults to UTC).
  • An object with no availability is always available.

This is how time-of-day menus work — there’s one menu per location, and availability windows carve it up.

Happy hour: a recurring discount

A recurring discount takes three top-level objects working together — what’s targeted (a product set), the discount math, and when it’s active (the promotion):

{
"productSets": [
{ "externalId": "set-drinks", "name": "All drinks", "itemIdsAny": ["itm-latte", "itm-cold-brew"] }
],
"discounts": [
{
"externalId": "disc-happy-hour",
"name": "20% off drinks",
"benefitType": "percentage",
"percentage": 20,
"benefitTargetProductSetId": "set-drinks"
}
],
"promotions": [
{
"externalId": "promo-happy-hour",
"name": "Happy hour",
"discountId": "disc-happy-hour",
"applicationMode": "automatic",
"availability": {
"windows": [
{ "dayOfWeek": 1, "startTime": "16:00", "endTime": "18:00" },
{ "dayOfWeek": 2, "startTime": "16:00", "endTime": "18:00" },
{ "dayOfWeek": 3, "startTime": "16:00", "endTime": "18:00" },
{ "dayOfWeek": 4, "startTime": "16:00", "endTime": "18:00" },
{ "dayOfWeek": 5, "startTime": "16:00", "endTime": "18:00" }
]
}
}
]
}
  • The product set says what the discount applies to (here, two drink items; use "allProducts": true for the whole menu).
  • The discount says how much (20% off). Other benefitTypes include amount, set_price, free_item, and bxgy.
  • The promotion ties them together and says when: applicationMode: "automatic" applies it without a code, and availability gives the recurring weekly windows. Use validFrom / validUntil for a one-off campaign instead.

86 an item

Mark something out of stock without restructuring the menu — set stockStatus on the item (or variation):

{ "externalId": "itm-latte", "name": "Latte", "stockStatus": "out_of_stock", "variations": [{ "externalId": "var-latte", "name": "Regular", "price": { "amount": { "amountMinor": 450, "currency": "USD" } } }] }

stockStatus is in_stock, out_of_stock, or low_stock. Omitting it on a re-publish preserves the current value, so a stock update can be a small, targeted publish.

Combine them

These compose. A breakfast category (availability) can hold a build-your-own item (nested modifiers) with a “first topping free” group (freeSelectionCount), all while a happy-hour promotion runs on drinks. Publish the whole menu and Maple applies it as one diff — see Publish a menu.