Menu field reference

Reference for every object in the Menu API. "Sync" columns describe what you send to POST /v1/sync; "Read" notes describe how the object comes back from GET /v1/menu.

Conventions

All money fields (priceMinor, priceAdjustment) are integers in minor currency units (kopecks, cents) — 21500 means 215.00. All objects are matched and returned by externalId (your identifier); items created manually in the DuckHub app have externalId: null in read responses.

Category#

FieldTypeSyncNotes
externalIdstringRequired, ≤ 255Unique per venue; upsert key
namestringRequired, ≤ 200
sortOrderintegerOptionalDefault 0 on create; lists are sorted by it

Ingredient#

FieldTypeSyncNotes
externalIdstringRequired, ≤ 255Unique per venue; upsert key
namestringRequired, ≤ 200
sortOrderintegerOptionalDefault 0 on create

Ingredients serve two purposes: product composition (via ingredientExternalIds on a product) and modifier options (via modifierGroups[].options[].ingredientExternalId).

Product#

FieldTypeSyncNotes
externalIdstringRequired, ≤ 255Unique per venue; upsert key; re-syncing a cleaned-up product restores it
namestringRequired, ≤ 200
descriptionstringOptional, ≤ 1000
priceMinorintegerRequired, ≥ 0Minor units
categoryExternalIdstringOptional, ≤ 255Unknown id → saved without category (warning). Omitting it on update clears the category link
ingredientExternalIdsstring[]OptionalComposition. Omit = keep as is; [] = clear; list = replace (unknown ids skipped with warning)
modifierGroupsModifierGroup[]OptionalOmit = keep as is; [] = clear; list = replace all groups
sortOrderintegerOptionalDefault 0 on create
menuVisiblebooleanOptionalDefault true on create; false hides from published menu; set by cleanup deactivation

Read shape adds: ingredients[] as { externalId, name } objects and fully expanded modifierGroups (see below).

ModifierGroup#

FieldTypeSyncNotes
namestringRequired, ≤ 200
typestringRequiredsingle_choice | multiple_choice | add_ingredients | remove_ingredients
isRequiredbooleanOptionalOnly single_choice groups can be required — there it defaults to true; for all other types the server forces false
optionsModifierOption[]Required
sortOrderintegerOptionalDefaults to the group's position in the array

Group types:

TypeGuest behaviour
single_choicePick exactly one option (e.g. size); can be required
multiple_choicePick any number of options
add_ingredientsAdd extra ingredients (typically priced)
remove_ingredientsRemove ingredients (options default to action: "remove")

Syncing a product with modifierGroups replaces its groups wholesale — groups are not merged or matched individually.

ModifierOption#

FieldTypeSyncNotes
ingredientExternalIdstringRequired, ≤ 255Must reference a synced ingredient; unknown → option skipped (warning)
actionstringOptionaladd | remove; default remove in remove_ingredients groups, else add
priceAdjustmentintegerOptionalMinor units, may be negative; default 0
sortOrderintegerOptionalDefaults to the option's position

Read shape adds ingredientName (denormalised from the ingredient).

Read-only response fields#

FieldWhereMeaning
generatedAtGET /v1/menuServer timestamp of the response
syncedAtPOST /v1/sync responseServer timestamp of the sync
publicationIdPOST /v1/publish responseId of the created snapshot