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

EndpointPUT /api/v1/brands/{brand_id}/menus/{external_id} (the synchronous upload, where the menu is in the request body)
Not supported onthe 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.
EnablementOff 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 ITEM in the same menu
    payload. This is the source of truth for the product.
  • Inheriting child — a CHOICE item 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:

  1. 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 (modifier item_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.
  2. price_info.overrides — context-specific price overrides.

Everything else is inheritedname, 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:

  1. The child must have its own id.
  2. The child must reference a parent that exists in the same payload.
  3. The child cannot inherit from itself.
  4. No chains — the parent must not itself use inherit_from.
  5. The parent must be type ITEM (or omit type).
  6. The child's type must be CHOICE or omitted (it is forced to CHOICE).
  7. The child must be referenced from a bundle-item modifier (that's what
    makes it a bundle choice).
  8. The child must not appear in any category's item_ids (it isn't a
    standalone, browsable item).
  9. Every choice offered through the child's modifier_ids must also be offered
    by the parent (compared at the modifier item_ids level — the child may
    keep, drop or swap modifier groups, but must not introduce a new choice).
  10. The child may only set modifier_ids and price_info.overrides — nothing
    else (see above).
  11. The child must not set a base price (price_info.price); it is
    inherited.
  12. 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_ids to 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 a pizza_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_reasonMeaning
inherit_from_feature_not_enabledThe feature isn't enabled for your integrator.
inherit_from_child_missing_idAn item with inherit_from has no id.
inherit_from_self_referenceAn item inherits from itself.
inherit_from_parent_not_foundThe referenced parent id isn't in this payload.
inherit_from_chain_not_allowedThe parent itself uses inherit_from (chains aren't allowed).
inherit_from_invalid_parent_typeThe parent isn't an ITEM.
inherit_from_invalid_child_typeThe inheriting item's type is set to something other than CHOICE.
inherit_from_child_in_categoryThe inheriting item appears in a category's item_ids.
inherit_from_child_not_a_bundle_childThe inheriting item isn't referenced from a bundle-item modifier.
inherit_from_modifier_choices_not_subsetThe child's modifiers offer a choice the parent doesn't (compared at the modifier item_ids level).
inherit_from_field_not_overridableThe child sets an inherited field (anything other than modifier_ids / price_info.overrides). The message lists the offending fields.
inherit_from_base_price_not_overridableThe child set price_info.price; the base price is inherited.
inherit_from_invalid_price_infoThe child's price_info is malformed (not an object).
duplicate_item_idTwo 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 ITEM and the inheriting CHOICE in the same upload —
    inherit_from resolves 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_from only 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.