Menus¶
API Spec
The implementable contract is in the Merchant API Spec — English only.
Part of Merchant
Menus and Store data make up the merchant capability (see overview). They are not Discovery extensions.
This page covers the catalog: menus, categories, item-offers, option-groups, and options. Services and pause: Store data.
Breaking V1 → V2 (catalog)¶
End of monolithic merchantUpdate
In V1, catalog updates used the merchantUpdate / menuUpdated webhook with a full payload. In V2 the normative path is CRUD per entity + GET …/snapshot for bootstrap.
| Field / model | V1 | V2 |
|---|---|---|
| Publication | Monolithic webhook | CRUD + snapshot + PUT snapshot |
optionPrice |
Optional | Required (0 if free) |
Option subtotal |
Present / confusing | Removed |
unityPrice |
Implicit | Explicit |
quantityAvailable |
— | New (operational) |
| Images in categories | No | New |
| Subcategories | No | New (category nesting) |
Hierarchy¶
Merchant
├── Service (DELIVERY / TAKEOUT / INDOOR) → menuId
└── Menu
└── Category
├── Subcategory (new — category nesting)
└── ItemOffer
└── OptionGroup (recursive)
└── Option → OptionGroup…
A merchant may have multiple menus. Each Service may reference the active menu via menuId.
Subcategories (new)¶
Categories can have subcategories (one level of nesting). Useful for structures like: - Burgers > Classics, Specials - Beverages > Hot, Cold
Snapshot vs CRUD vs PUT¶
| Scenario | Approach | operationId |
|---|---|---|
| Initial load / full reconciliation | Full snapshot | getMenuSnapshot |
| Update entire catalog after major change | PUT full snapshot | replaceMenuSnapshot |
| List menus | Listing | listMenus |
| Price / name / availability | PATCH/PUT entity | updateItemOffer, … |
| New item / category / option | POST | createItemOffer, createCategory, createOption |
| Removal | DELETE (async 202) |
deleteItemOffer, … |
New in V2: Instead of the V1 pattern "update locally → notify via webhook → OA polling", use PUT snapshot:
sequenceDiagram
participant SS as Software Service
participant OA as Ordering Application
Note over SS,OA: V2 — Push snapshot
SS->>OA: PUT …/menus/{menuId}/snapshot
OA-->>SS: 202 Accepted
Note over OA: Process full catalog
Note over SS,OA: Fallback — Pull snapshot
OA->>SS: GET …/menus/{menuId}/snapshot
SS-->>OA: 200 MenuSnapshot
Note over OA: Bootstrap / error recovery
GET /merchants/{merchantId}/menus/{menuId}/snapshot
The snapshot returns the hierarchy (categories → item-offers → option-groups → options). It is the practical replacement for the V1 “full menu” for bootstrap, not a return of the monolithic webhook.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
OA->>SS: GET …/menus/{menuId}/snapshot
SS-->>OA: 200 MenuSnapshot
Note over OA: local bootstrap
OA->>SS: PATCH …/item-offers/{id} { unity_price }
SS-->>OA: 202 Accepted
ItemOffer and pricing¶
| Field | Required | Notes |
|---|---|---|
unityPrice |
YES | Base price in minor units |
quantityAvailable |
NO | Operational signal (e.g. 10 portions left). Not multi-channel stock. Omitted/null = no declared limit; 0 = unavailable |
status |
YES | AVAILABLE / UNAVAILABLE |
externalCode |
NO | POS internal code |
imageUrl |
NO | Item image URL (new in V2) |
OptionGroup and Option (recursive)¶
OptionGroups may nest (e.g. size → doneness → sauce). Real-world depth is usually 2–3 levels.
optionPrice required in V2
Every Option MUST have optionPrice. No extra cost: 0. V1 option subtotal is removed — on the order, use unityPrice + sum of optionPrice (see Orders).
Operations map (catalog)¶
| Goal | operationId |
|---|---|
| List menus | listMenus |
| Snapshot (GET / PUT) | getMenuSnapshot · replaceMenuSnapshot |
| Categories | listCategories · createCategory · replaceCategory · deleteCategory |
| Subcategories | listSubcategories · createSubcategory · replaceSubcategory · deleteSubcategory |
| Item offers | listItemOffers · createItemOffer · replaceItemOffer · updateItemOffer · deleteItemOffer |
| Option groups | listOptionGroups · createOptionGroup · replaceOptionGroup · deleteOptionGroup |
| Options | listOptions · createOption · replaceOption · deleteOption |
Sync model¶
Who is the source of truth for the catalog (POS vs originator) must be clear in Discovery and the commercial contract. The protocol:
- Does not reintroduce
merchantUpdateas the V2 core path - Does not define a catalog-delta webhook in the MVP
- Reconciliation: snapshot + CRUD
Async mutations return 202; synchronous creates may return 201 with a body.
Checklists¶
Checklist — Ordering Application
- [ ] Bootstrap with snapshot
- [ ] Deltas via CRUD, not monolithic webhook
- [ ] Always expect
option_price - [ ] Treat
quantity_availableas a hint, not stock guarantee
Checklist — Software Service
- [ ] Referential integrity menu → … → options
- [ ]
202on async mutations - [ ] No
merchantType/ no V1 optionsubtotal
Out of MVP¶
| Topic | Status |
|---|---|
| Catalog delta notification webhook | Not core-normative |
| Free-form custom fields | Out of MVP |
| Multi-channel stock control | Out of scope |