modifier_group_ids, and its pricing.modifiers entry describes how it appears inside the group that lists it.
What this example includes
| Level | Parent / group | Modifier product | Nested group |
|---|---|---|---|
| 1 | meal-combo → choose-burger | cheeseburger | choose-patty |
| 2 | cheeseburger → choose-patty | beef-patty | choose-doneness |
| 3 | beef-patty → choose-doneness | medium-rare | — |
Request
All products and groups live in the same Upsert menus payload. Attach the next group on the modifier product the customer selects at the previous level.curl --request PUT \
--url 'https://firespark.cloud/api/integrations/v1/menus?fulfillment_id=delivery&store_id=downtown&channel_id=app&brand_id=0001' \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d @multi-level-modifiers.json
multi-level-modifiers.json
{
"style": "DEFAULT",
"products": [
{
"id": "meal-combo",
"name": { "en_us": "Meal combo" },
"description": { "en_us": "Burger and a drink." },
"image_url": null,
"status": "ACTIVE",
"bundled_items": [],
"pricing": {
"minimum_quantity": 1,
"maximum_quantity": 5,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 10.99,
"anchor_price": 10.99,
"anchor_price_percentage": 0,
"modifier_group_ids": ["choose-burger"],
"modifiers": []
},
"availability": {
"status": "AVAILABLE",
"schedules": null,
"out_of_stock_until": null
},
"metadata": {}
},
{
"id": "cheeseburger",
"name": { "en_us": "Cheeseburger" },
"description": null,
"image_url": null,
"status": "ACTIVE",
"bundled_items": [],
"pricing": {
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"modifier_group_ids": ["choose-patty"],
"modifiers": [
{
"modifier_group_id": "choose-burger",
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"default_quantity": 0,
"charge_above_quantity": 0,
"display_type": "RADIO"
}
]
},
"availability": {
"status": "AVAILABLE",
"schedules": null,
"out_of_stock_until": null
},
"metadata": {}
},
{
"id": "veggie-burger",
"name": { "en_us": "Veggie burger" },
"description": null,
"image_url": null,
"status": "ACTIVE",
"bundled_items": [],
"pricing": {
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"modifier_group_ids": [],
"modifiers": [
{
"modifier_group_id": "choose-burger",
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"default_quantity": 0,
"charge_above_quantity": 0,
"display_type": "RADIO"
}
]
},
"availability": {
"status": "AVAILABLE",
"schedules": null,
"out_of_stock_until": null
},
"metadata": {}
},
{
"id": "beef-patty",
"name": { "en_us": "Beef patty" },
"description": null,
"image_url": null,
"status": "ACTIVE",
"bundled_items": [],
"pricing": {
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"modifier_group_ids": ["choose-doneness"],
"modifiers": [
{
"modifier_group_id": "choose-patty",
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"default_quantity": 0,
"charge_above_quantity": 0,
"display_type": "RADIO"
}
]
},
"availability": {
"status": "AVAILABLE",
"schedules": null,
"out_of_stock_until": null
},
"metadata": {}
},
{
"id": "veggie-patty",
"name": { "en_us": "Veggie patty" },
"description": null,
"image_url": null,
"status": "ACTIVE",
"bundled_items": [],
"pricing": {
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"modifier_group_ids": [],
"modifiers": [
{
"modifier_group_id": "choose-patty",
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"default_quantity": 0,
"charge_above_quantity": 0,
"display_type": "RADIO"
}
]
},
"availability": {
"status": "AVAILABLE",
"schedules": null,
"out_of_stock_until": null
},
"metadata": {}
},
{
"id": "medium-rare",
"name": { "en_us": "Medium rare" },
"description": null,
"image_url": null,
"status": "ACTIVE",
"bundled_items": [],
"pricing": {
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"modifier_group_ids": [],
"modifiers": [
{
"modifier_group_id": "choose-doneness",
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"default_quantity": 0,
"charge_above_quantity": 0,
"display_type": "RADIO"
}
]
},
"availability": {
"status": "AVAILABLE",
"schedules": null,
"out_of_stock_until": null
},
"metadata": {}
},
{
"id": "well-done",
"name": { "en_us": "Well done" },
"description": null,
"image_url": null,
"status": "ACTIVE",
"bundled_items": [],
"pricing": {
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"modifier_group_ids": [],
"modifiers": [
{
"modifier_group_id": "choose-doneness",
"minimum_quantity": 0,
"maximum_quantity": 1,
"is_tax_inclusive": true,
"tax_rate": 0.15,
"price": 0,
"anchor_price": 0,
"anchor_price_percentage": 0,
"default_quantity": 0,
"charge_above_quantity": 0,
"display_type": "RADIO"
}
]
},
"availability": {
"status": "AVAILABLE",
"schedules": null,
"out_of_stock_until": null
},
"metadata": {}
}
],
"categories": [
{
"id": "combos",
"name": { "en_us": "Combo meals" },
"description": null,
"status": "ACTIVE",
"items": [{ "type": "PRODUCT", "id": "meal-combo" }],
"availability": {
"status": "AVAILABLE",
"schedules": null,
"out_of_stock_until": null
},
"metadata": {}
}
],
"modifier_groups": [
{
"id": "choose-burger",
"title": { "en_us": "Choose a burger" },
"description": null,
"display_type": "LIST",
"modifier_ids": ["cheeseburger", "veggie-burger"],
"minimum_quantity": 1,
"maximum_quantity": 1,
"charge_above_quantity": null,
"metadata": {}
},
{
"id": "choose-patty",
"title": { "en_us": "Choose a patty" },
"description": null,
"display_type": "LIST",
"modifier_ids": ["beef-patty", "veggie-patty"],
"minimum_quantity": 1,
"maximum_quantity": 1,
"charge_above_quantity": null,
"metadata": {}
},
{
"id": "choose-doneness",
"title": { "en_us": "Choose doneness" },
"description": null,
"display_type": "LIST",
"modifier_ids": ["medium-rare", "well-done"],
"minimum_quantity": 1,
"maximum_quantity": 1,
"charge_above_quantity": null,
"metadata": {}
}
],
"menus": [
{
"id": "all-day-delivery",
"name": { "en_us": "All-day delivery" },
"description": null,
"schedules": null,
"metadata": {},
"category_ids": ["combos"],
"status": "ACTIVE"
}
]
}
Key points
- Nest through modifier products. Each level is a real product with its own external
idfor kitchen tickets and order injection. modifiersdescribes the option in a group.beef-pattycarries apricing.modifiersentry withmodifier_group_id: "choose-patty".modifier_group_idsopens the next question.cheeseburgerlistschoose-pattyso patty choices appear after the customer picks cheeseburger.- Leaf modifiers have empty nested groups.
medium-raresetsmodifier_group_ids: []because the chain ends there. - Branching paths.
veggie-burgerskips patty and doneness groups because it has no nestedmodifier_group_ids.
Related
Products — modifier groups
How groups and modifiers relate
Simple menu
Single-level modifier example