Learn how to nest modifier lists under modifiers with the Catalog API to build multi-step customization flows, and how to read and search the modifier tree.
Manage Nested Modifiers
Applies to: Catalog API
A flat modifier list presents every option at once. That works for a latte, but not for a burrito where choosing steak should lead to a sauce question, and choosing green salsa should lead to a spice level question. Nested modifiers let one selection reveal the next, so the buyer answers a sequence of questions instead of reading one long list.
Nesting is expressed on the modifier that triggers it. A CatalogModifier has a child_modifier_list_ids field holding references to the CatalogModifierList objects that should be presented when that modifier is selected. Nothing about the modifier lists themselves changes - a nested list is an ordinary modifier list that happens to be referenced by a modifier.
Important
Set the Square-Version header to 2026-08-19 or later. Earlier versions cannot write child_modifier_list_ids, and Square omits the field from their responses.
This page assumes you're familiar with the modifier data model. See Enable Item Customization with Modifiers for the basics.
Each entry in child_modifier_list_ids is the ID of a modifier list to present when the parent modifier is selected. The following rules govern the field:
| Rule | Detail |
|---|---|
| Up to 5 children per modifier | A single modifier can reference at most 5 modifier lists. |
| Up to 3 levels of nesting | Counted in modifier lists, not modifiers. See Nesting depth. |
| No duplicates | The same modifier list ID cannot appear twice in one modifier's child_modifier_list_ids. |
| No cycles | A modifier cannot reference a modifier list that is already one of its own ancestors, including the list the modifier itself belongs to. |
| Order is preserved | Square stores the order you send and returns the entries in that order. |
A modifier list can be referenced by more than one modifier, and a list that's nested under a modifier can also be attached directly to items. In the burrito example that follows, both Steak and Chicken reference the same Protein Amount list rather than each needing its own copy.
Depth counts modifier list levels along a chain of references. The list at the top of a chain is level 1, a list referenced by one of its modifiers is level 2, and a list referenced by one of those modifiers is level 3:
Protein (level 1) └── Steak ├── Sauce (level 2) │ └── Green Salsa │ └── Spice Level (level 3) └── Protein Amount (level 2)
A modifier in Spice Level cannot reference a further modifier list, because the resulting list would sit at level 4.
Attaching a modifier list to an item does not consume a level - the item is not part of the chain. Protein is level 1 whether it's attached to one item, many items, or none.
Two consequences are worth planning for:
- Depth is evaluated over the whole tree as it exists in the catalog, not just the objects in your request. Adding a child to a modifier whose ancestors already exist in the catalog can fail even though your request contains a single object.
- When a modifier list is reachable by more than one path, its deepest path determines its level. Referencing a shared list from a new, deeper location can push it past the limit and fail, even though your request didn't touch the branch that was already valid.
Requests that break the depth limit or introduce a cycle are rejected with a 400 response and an INVALID_VALUE error code.
The following BatchUpsertCatalogObjects request builds the burrito tree in a single call:
Spice Level, withMildandHot.Sauce, whoseGreen Salsamodifier nestsSpice Level.Protein Amount, withSingleandDouble.Protein, whoseSteakmodifier nests bothSauceandProtein Amount, and whoseChickenmodifier nests onlyProtein Amount.- A
Burritoitem that attachesProtein. The three nested lists don't need to be attached to the item - they're reached through the tree.
Batch upsert catalog objects
Steak returns child_modifier_list_ids in the order it was sent - Sauce before Protein Amount.
child_modifier_list_ids behaves like every other catalog field: the list you send replaces the stored list in full. To add or remove one child, read the parent modifier list, modify the array on the relevant modifier, and upsert the object with its current version. Omitting the field clears the modifier's children.
Deleting a modifier list that's nested under a modifier isn't blocked. Square removes the reference from every modifier that pointed to it, and, as with any modifier list, deletes the modifiers the list contained. Modifier lists that were nested one level deeper survive the delete and become the top of their own chain, so re-parent them if they should stay reachable.
A modifier tree is a graph of references, so reading it with BatchRetrieveCatalogObjects or SearchCatalogObjects alone means one round trip per level. The include_options request field collapses that into a single call. It takes an include array, and Square returns what you asked for in a new included_resources object alongside the usual objects.
include_options is supported on BatchRetrieveCatalogObjects, SearchCatalogObjects, and SearchCatalogItems, and accepts two values:
| Include type | Returned in | Description |
|---|---|---|
INCLUDE_NESTED_MODIFIERS | included_resources.nested_modifiers | The modifier lists below the requested objects. |
INCLUDE_ANCESTOR_MODIFIERS | included_resources.ancestor_modifiers | The modifier lists above the requested modifier lists. |
Included objects are complete CatalogObjects with modifier_list_data and their modifiers inlined, so each returned modifier carries its own child_modifier_list_ids and you can reconstruct the tree from a single response.
What INCLUDE_NESTED_MODIFIERS returns depends on the type of each requested object:
| Requested object | Returned in nested_modifiers |
|---|---|
ITEM | The modifier lists attached to the item, plus every modifier list nested beneath them. |
MODIFIER_LIST | Every modifier list nested beneath it. The requested list itself isn't repeated here. |
MODIFIER | The modifier lists it references, plus every modifier list nested beneath those. |
The following example retrieves only the Burrito item and gets its entire modifier tree back in one call:
Batch retrieve catalog objects
INCLUDE_ANCESTOR_MODIFIERS walks the tree in the other direction. For each CatalogModifierList among the requested objects, Square returns the modifier lists above it, following parent references all the way to the top of the chain. Use it to answer "where does this list appear?" - for example, to warn a seller editing Spice Level that the change affects everything reached through Protein.
ancestor_modifiers contains CatalogModifierList objects. The parent modifier that does the nesting is returned inside its own list's modifier_list_data.modifiers, so to find the exact modifier that references your list, look for the one whose child_modifier_list_ids contains it.
Batch retrieve catalog objects
A modifier list with more than one parent returns all of them, along with each parent's own ancestors. A modifier list that isn't nested anywhere returns an empty ancestor_modifiers.
INCLUDE_ANCESTOR_MODIFIERS gives you the ancestor lists of a list you're already retrieving. When you instead need the specific modifiers that reference a list - the ones a seller would have to edit to detach it - use the modifiers_for_child_list_query filter on SearchCatalogObjects. It takes child_modifier_list_ids and returns the CatalogModifier objects whose child_modifier_list_ids contain any of them.
The following example finds the modifiers that nest the Sauce list:
Search catalog objects
The filter returns direct parents only. To climb further, either run the query again against the modifier list each returned modifier belongs to (modifier_data.modifier_list_id), or retrieve that list with INCLUDE_ANCESTOR_MODIFIERS.