Skip to main content
A menu is the structured description of everything a customer can order at a location: the items, how they’re priced, how they’re customized, and which taxes and fees apply. You publish it as one JSON document, and Maple keys every object off the external IDs you assign. This page explains the model and how to map your own catalog onto it. The Publish a menu guide covers the request flow, Menu recipes has worked examples, and the endpoint reference lists every field.

One menu per location

Each location has exactly one menu. There’s no concept of multiple named menus (a separate “breakfast menu” and “dinner menu”) for a location — time-of-day differences are expressed within the single menu using availability windows. Publishing replaces that one menu as a whole (applied as a diff — see below). Maple keeps version history internally and always serves the current version, but from your side the model is simple: one location, one menu, which you publish in full.
If your own system models several menus per location, flatten them into one Maple menu before publishing — typically as separate categories with their own availability windows.

The tree

A menu is a tree of three nested objects, with shared definitions referenced from inside it:
  • Category — a menu section (Drinks, Mains). Can be dayparted so it only appears in certain windows.
  • Item — a product. Has one or more variations and may carry modifier groups, tax, and fees.
  • Variation — a buyable form of an item (Small / Large, or just “Regular”). The price lives here. Every item has at least one.
  • Modifier group — a set of choices (Milk, Toppings) with selection rules. Defined once and shared, or inline on an item. Options can themselves carry nested modifier groups.

Mapping your own catalog

Most integrators already have a menu model. Here’s how typical concepts map — getting this right up front avoids the most common mistakes:
Your conceptMaps toNotes
Menu section / headingCategoryOrder matters; categories can be dayparted.
Product / dishItemA name and description; not where the price lives.
Size / option that changes the base price (Small/Large)Variation of the itemNot a separate item. Every item needs ≥1 variation, even if it’s just “Regular”.
Add-on / customization (extra shot, no onions)Modifier option in a modifier groupGroup carries the selection rules; option carries any price change.
A reusable choice set used on many items (Milk)Shared modifier group referenced by groupIdDefine once, reference everywhere.
Combo / build-your-own with sub-choicesNested modifier groupsOptions can carry their own modifier groups.
Common wrong assumptions to avoid: a price does not live on the item (it lives on the variation); a size is not a separate item (it’s a variation); and there is one menu per location (dayparting is availability windows, not separate menus).

External IDs are yours

Every object — category, item, variation, modifier group, option, tax rate, fee — carries an externalId that you assign and own. Maple never changes it. This is the backbone of the model:
  • Re-publishing with the same externalId updates the same object instead of creating a duplicate.
  • Order line items reference the menu by these IDs (menu_entity_id), so an incoming order maps directly back to your catalog.
  • Maple also mints its own IDs (item_…, var_…, ctg_…) in responses for reference, but you address everything by your externalId.
  • Objects reference each other by externalId too. When a modifier group reference uses groupId, an item points at a tax rate with taxRateIds, or a promotion names its discount with discountId, those values are the externalIds you assigned — not Maple’s minted IDs.
Use IDs that are stable in your own system (a SKU, a primary key) — not the display name, which can change.

Pricing

Prices are objects, not bare numbers, so the model can express more than a flat amount. The base shape is an amount in integer minor units with a currency:
{ "amount": { "amountMinor": 450, "currency": "USD" } }
The price object also supports price types (fixed, variable open-amount, per_unit for weighed items, bulk quantity tiers), tax-inclusive pricing, per-currency overrides, and a compare-at (strikethrough) amount. For the common case — a fixed price in one currency — you only need amount. See Money and amounts.

Modifiers

A modifier group has selection rules and a set of options. The rules are richer than a simple min/max, which is what lets you express things like “choose up to 3, but no more than 2 of the same”:
FieldControls
minSelections / maxSelectionsTotal selections allowed (counting quantities).
minUniqueSelections / maxUniqueSelectionsHow many distinct options may be chosen.
freeSelectionCountHow many selections are free before charges apply.
allowedQuantitiesPermitted per-option quantities.
option minQuantity / maxQuantityBounds on a single option.
Reference a shared group from an item or variation by groupId, or define one inline. The two shapes are mutually exclusive in a single entry — a reference carries only groupId; a definition carries name and options. Mixing them is rejected rather than silently truncated. Options can carry their own nested modifier groups for build-your-own flows. See Menu recipes for worked examples.
Order resources currently expose the directly-selected, first-level modifiers on each line item. Deeper nested selections aren’t expanded into the order resource yet.

Tax and fees

These are top-level definitions you attach to items by ID:
  • Tax categories and rates — rates can be percentage, flat, or bracket-based, on a configurable base, inclusive or exclusive. Attach with taxCategoryId / taxRateIds.
  • Fees — surcharges and deposits (a bottle deposit or CRV), attached with feeIds.
  • Discounts, promotions, and coupons — optional top-level collections for price reductions.
Most menus need a single tax rate and no fees; the richer constructs are there when a location needs them.

Availability and dayparting

Availability windows are how you express time-of-day menus — this replaces the idea of multiple menus.
  • A category with an availability window only appears during it (breakfast until 11:00).
  • An item can carry its own windows too.
  • An object with no availability is always available.
Each window is a day plus a local time range:
  • dayOfWeek0 (Sunday) through 6 (Saturday). Add one window per active day.
  • startTime / endTime — local HH:MM, 24-hour.
Windows are evaluated in the menu’s timezone, set by the top-level timezone field (IANA, e.g. America/New_York). It defaults to UTC if unset, so set it on your first publish — see Set the timezone for availability. A “daily special” or breakfast section is a category (or item) with the right windows, living in the one menu. See the daily-specials recipe.

Stock status

Stock status is in_stock, out_of_stock, or low_stock. On publish, omitting it preserves the current value; brand-new objects default to in_stock. Use it to 86 an item without republishing the whole menu’s structure.

Publishing replaces the whole menu

You always send the entire menu as the desired state. Maple applies it as a diff keyed by externalId:
  • Validation is all-or-nothing — a 400 lists every issue and changes nothing.
  • An identical re-publish is a no-op, and object identities stay stable, so you can publish on every menu change without churn.
So the simplest integration re-publishes the full menu whenever your source changes. See Publish a menu.