Bundle item variants with inherit_from
Reuse a standalone item as a bundle-only CHOICE with a subset of its modifiers and context pricing, without duplicating its attributes.
What it is
inherit_from is authoring sugar for menu uploads. It lets you declare that a
CHOICE item inside a bundle is the same product as one of your standalone
items, without re-typing all of that product's attributes.
A common case: you sell a Margherita Pizza as a standalone item, and you also
offer it as one of the choices inside a Meal Deal bundle — but inside the
bundle the customer can only pick a size (not the full set of toppings), and it
may be priced differently. Instead of duplicating the pizza's name, description,
allergens, tax rate, image, nutritional info and so on into the bundle choice,
you point the bundle choice at the standalone item with inherit_from, and the
platform fills in the rest for you.
The platform expands inherit_from server-side: the inheriting item is
materialised into a full copy of the parent before the menu is processed.
inherit_from itself is never stored or forwarded downstream — it exists only
in the payload you send.
Availability
| Endpoint | PUT /api/v1/brands/{brand_id}/menus/{external_id} (the synchronous upload, where the menu is in the request body) |
| Not supported on | the async/large-payload upload flow (PUT /api/v3/..., which returns a presigned URL). A payload containing inherit_from sent through that flow is rejected — see Async uploads. |
| Enablement | Off by default. It is enabled per integrator. While it is off, any payload containing inherit_from is rejected with inherit_from_feature_not_enabled. Contact your integration manager to have it switched on. |
The model
There are two roles:
- Parent — an existing standalone item of type
ITEMin the same menu
payload. This is the source of truth for the product. - Inheriting child — a
CHOICEitem used inside a bundle that copies the
parent.
The child item ends up as a full clone of the parent. The only things you
are allowed to make different on the child are:
modifier_ids— the modifiers may only narrow the parent's: every
choice offered through the child's modifiers must also be offered by the
parent. This is compared at the choice level (modifieritem_ids), not
the modifier-id level — so to offer a product in fewer sizes inside a bundle
you point the child at a narrower size modifier (e.g. a medium-only group),
which is allowed as long as it introduces no choice the standalone lacks.price_info.overrides— context-specific price overrides.
Everything else is inherited — name, description, image, plu,
barcodes, tax_rate, allergies, classifications, diets,
contains_alcohol, nutritional_info, operational_name, the base price
(price_info.price), and any other item field. If you set any of those on the
inheriting child, the upload is rejected (so the bundle choice can never
silently drift from the standalone product).
Rules and constraints
All of these are enforced server-side; violating any returns HTTP 400 with a
stable error_reason:
- The child must have its own
id. - The child must reference a parent that exists in the same payload.
- The child cannot inherit from itself.
- No chains — the parent must not itself use
inherit_from. - The parent must be type
ITEM(or omittype). - The child's
typemust beCHOICEor omitted (it is forced toCHOICE). - The child must be referenced from a
bundle-itemmodifier (that's what
makes it a bundle choice). - The child must not appear in any category's
item_ids(it isn't a
standalone, browsable item). - Every choice offered through the child's
modifier_idsmust also be offered
by the parent (compared at the modifieritem_idslevel — the child may
keep, drop or swap modifier groups, but must not introduce a new choice). - The child may only set
modifier_idsandprice_info.overrides— nothing
else (see above). - The child must not set a base price (
price_info.price); it is
inherited. - Item
ids in the payload must be unique.
Worked example
The scenario: the standalone Margherita Pizza comes in small, medium and
large via a single size modifier. Inside a Meal Deal bundle it is only
offered in medium, so the bundle child points at a separate, narrower size
modifier (size_meal_deal) that lists only the medium choice. This is valid
because the child's only size choice (pizza_medium) is also offered by the
parent — the modifier ids differ, but no new choice is introduced.
The check is at the choice level: the platform resolves each item's
modifier_idsto the choices (item_ids) inside those modifiers and requires
the child's choices to be a subset of the parent's. So you narrow options by
referencing a narrower modifier, not by editing the parent's modifier.
What you send
{
"items": [
{
"id": "pizza_margherita",
"type": "ITEM",
"name": { "en": "Margherita Pizza" },
"description": { "en": "Tomato, mozzarella, basil" },
"plu": "PIZ-001",
"tax_rate": "20.0",
"allergies": ["gluten", "milk"],
"contains_alcohol": false,
"modifier_ids": ["size"],
"price_info": { "price": 1200 }
},
{
"id": "mealdeal_pizza_margherita",
"inherit_from": "pizza_margherita",
"modifier_ids": ["size_meal_deal"],
"price_info": {
"overrides": [
{ "type": "PICKUP_ITEM", "id": "pickup", "price": 800 }
]
}
}
],
"modifiers": [
{
"id": "size",
"type": "size-modification",
"name": { "en": "Choose your size" },
"item_ids": ["pizza_small", "pizza_medium", "pizza_large"]
},
{
"id": "size_meal_deal",
"type": "size-modification",
"name": { "en": "Size" },
"item_ids": ["pizza_medium"]
},
{
"id": "choose_your_pizza",
"type": "bundle-item",
"item_ids": ["mealdeal_pizza_margherita"]
}
],
"categories": [
{ "id": "pizzas", "item_ids": ["pizza_margherita"] }
]
}The size modifier offers pizza_small, pizza_medium, pizza_large; the
size_meal_deal modifier offers only pizza_medium. Since pizza_medium is
also offered by the parent, the narrowing is allowed. The inheriting child
carries only its id, the inherit_from pointer, its narrower modifier_ids,
and a price override — it does not repeat the name, allergens, tax rate, etc.,
and it does not restate the base price.
What the platform stores (after expansion)
{
"id": "mealdeal_pizza_margherita",
"type": "CHOICE",
"name": { "en": "Margherita Pizza" },
"description": { "en": "Tomato, mozzarella, basil" },
"plu": "PIZ-001",
"tax_rate": "20.0",
"allergies": ["gluten", "milk"],
"contains_alcohol": false,
"modifier_ids": ["size_meal_deal"],
"price_info": {
"price": 1200,
"overrides": [
{ "type": "PICKUP_ITEM", "id": "pickup", "price": 800 }
]
}
}Everything is inherited from the parent; type became CHOICE; modifier_ids
is your narrower set (medium only); the base price (1200) is inherited while
your pickup override (800) is applied; and inherit_from is gone.
If the bundle's size modifier listed a choice the standalone doesn't sell
(say apizza_xl), the upload is rejected with
inherit_from_modifier_choices_not_subset.
Price overrides
The base price always comes from the parent. To make the in-bundle price differ
by context, supply price_info.overrides — the standard override array, each
entry being:
{ "type": "ITEM | MODIFIER | PICKUP_ITEM | PICKUP_MODIFIER", "id": "<context-id>", "price": <minor-units> }You may not set price_info.price (base price) or any other price_info
field (e.g. fees) on an inheriting child — only overrides.
Error reference
error_reason | Meaning |
|---|---|
inherit_from_feature_not_enabled | The feature isn't enabled for your integrator. |
inherit_from_child_missing_id | An item with inherit_from has no id. |
inherit_from_self_reference | An item inherits from itself. |
inherit_from_parent_not_found | The referenced parent id isn't in this payload. |
inherit_from_chain_not_allowed | The parent itself uses inherit_from (chains aren't allowed). |
inherit_from_invalid_parent_type | The parent isn't an ITEM. |
inherit_from_invalid_child_type | The inheriting item's type is set to something other than CHOICE. |
inherit_from_child_in_category | The inheriting item appears in a category's item_ids. |
inherit_from_child_not_a_bundle_child | The inheriting item isn't referenced from a bundle-item modifier. |
inherit_from_modifier_choices_not_subset | The child's modifiers offer a choice the parent doesn't (compared at the modifier item_ids level). |
inherit_from_field_not_overridable | The child sets an inherited field (anything other than modifier_ids / price_info.overrides). The message lists the offending fields. |
inherit_from_base_price_not_overridable | The child set price_info.price; the base price is inherited. |
inherit_from_invalid_price_info | The child's price_info is malformed (not an object). |
duplicate_item_id | Two items share the same id. |
Async uploads
If you use the large-payload upload flow (PUT /api/v3/..., which hands you a
presigned URL), inherit_from is not supported — a payload containing it is
rejected with inherit_from is not supported on the async upload path. Expand
the items yourself (send the full CHOICE items), or use the synchronous
PUT /api/v1/... endpoint, which supports inherit_from.
Best practices
- Put the parent
ITEMand the inheritingCHOICEin the same upload —
inherit_fromresolves only within a single payload. Order doesn't matter
(the child may appear before the parent). - Point the bundle choice at modifiers that offer only what the customer can
actually pick inside the bundle — they must not offer any choice the
standalone item doesn't (e.g. a narrower, medium-only size modifier). - Use
inherit_fromonly for bundle choices that are genuinely the same product
as a standalone item — if the product differs in substance (different
allergens, different name), author it as a normal item instead.
