Modifiers
Modifiers are how a customer customizes a dish: pick a size, add toppings, choose a side, set a cooking preference. They are modeled in four parts so the same options can be reused across many items while still allowing per-item rules:
- A ModifierGroup is a reusable set of related choices ("Size", "Add-ons", "Choose a side").
- A ModifierOption is one choice within a group, with a
priceDelta. - An ItemModifierBinding attaches a group to a specific item, variant, or set — and can override the group's rules or hide individual options for that context.
- An AppliedModifier is the snapshot of what the customer actually chose, captured on the order item with its name and price.
Modifiers are demonstrated in the TypeScript SDK (client.modifiers). The Python menu guide does not include modifier examples; the schema below is the platform model that backs both.
Modifier groups
A group bundles related options with selection rules — minSelection, maxSelection, and isRequired.
const toppings = await client.modifiers.createGroup({
name: 'Toppings',
description: 'Add extra toppings',
isRequired: false,
minSelection: 0,
maxSelection: 3,
displayOrder: 1,
isActive: true,
options: [
{ name: 'Extra Cheese', priceDelta: 1.50, displayOrder: 1, isDefault: false, isActive: true },
{ name: 'Bacon', priceDelta: 2.00, displayOrder: 2, isDefault: false, isActive: true },
{ name: 'Avocado', priceDelta: 2.50, displayOrder: 3, isDefault: false, isActive: true },
],
});
const group = await client.modifiers.getGroup('group_123');
const groups = await client.modifiers.listGroups();
const updated = await client.modifiers.updateGroup('group_123', { id: 'group_123', description: 'Premium toppings' });
await client.modifiers.deleteGroup('group_123');
ModifierGroup fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | auto | Unique identifier |
name | string | Yes | Group name (e.g. "Size") |
description | string | null | No | Group description |
options | array | Yes | Options (min 1 required) |
minSelection | integer | No | Minimum selections (default 0) |
maxSelection | integer | null | No | Maximum selections |
isRequired | boolean | No | Whether a selection is required (default false) |
displayOrder | integer | No | Display order (default 0) |
isActive | boolean | No | Whether the group is active (default true) |
locationId | string | null | No | Location-specific group |
modifierRevisionId | string | null | No | Version control ID |
channelMappings | array | null | No | Per-channel external group IDs |
Validation: maxSelection must be ≥ minSelection and ≤ the number of options.
Modifier options
Options can also be managed individually within a group.
const option = await client.modifiers.createOption({
modifierGroupId: 'group_123',
name: 'Jalapenos',
description: 'Spicy sliced jalapenos',
priceDelta: 0.75,
displayOrder: 4,
isDefault: false,
isActive: true,
});
const options = await client.modifiers.getOptionsByGroup('group_123');
const updated = await client.modifiers.updateOption('option_123', { id: 'option_123', priceDelta: 1.00 });
await client.modifiers.deleteOption('option_123');
ModifierOption fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | auto | Unique identifier |
modifierGroupId | string | Yes | Parent group |
name | string | Yes | Option name (e.g. "Extra Cheese") |
description | string | null | No | Option description |
priceDelta | number | No | Price adjustment when selected (default 0) |
isDefault | boolean | No | Pre-selected (default false) |
displayOrder | integer | No | Order within group (default 0) |
isActive | boolean | No | Whether active (default true) |
locationId | string | null | No | Location-specific option |
Item modifier bindings
A binding attaches a group to a target — exactly one of a menu item or a menu set, optionally narrowed to a variant — and can override the group's rules for that context.
const binding = await client.modifiers.createBinding({
menuItemId: 'item_burger',
modifierGroupId: 'group_toppings',
displayOrder: 1,
isRequired: false,
});
const bindings = await client.modifiers.getBindingsByMenuItem('item_burger');
const updated = await client.modifiers.updateBinding('binding_123', { id: 'binding_123', displayOrder: 2, isActive: true });
await client.modifiers.deleteBinding('binding_123');
ItemModifierBinding fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | auto | Unique identifier |
modifierGroupId | string | Yes | Group to bind |
menuItemId | string | Conditional | Target item (exactly one of item or set) |
menuItemVariantId | string | null | No | Narrow to a variant (requires menuItemId) |
menuSetId | string | Conditional | Target set (exactly one of item or set) |
isRequiredOverride | boolean | null | No | Override the group's isRequired |
minSelectionOverride | integer | null | No | Override minSelection |
maxSelectionOverride | integer | null | No | Override maxSelection |
excludedOptionIds | array | null | No | Option IDs to hide in this context |
optionOverrides | array | null | No | Per-option overrides (below) |
displayOrder | integer | No | Display order (default 0) |
isActive | boolean | No | Whether active (default true) |
ModifierOptionOverride
A per-option override inside a binding re-prices or re-defaults a single option for that item.
| Field | Type | Required | Description |
|---|---|---|---|
modifierOptionId | string | Yes | Target option |
priceDeltaOverride | number | No | Override the price adjustment |
isDefaultOverride | boolean | No | Override default selection |
displayOrderOverride | integer | No | Override display order |
Applied modifiers
When a customer selects an option, the order item records an AppliedModifier — a snapshot that preserves the group and option names and the price applied, so the order stays accurate even if the menu changes later.
| Field | Type | Required | Description |
|---|---|---|---|
groupName | string | Yes | Modifier group name (preserved) |
optionName | string | Yes | Modifier option name (preserved) |
modifierGroupId | string | null | No | Internal group ID |
modifierOptionId | string | null | No | Internal option ID |
externalModifierGroupId | string | null | No | External platform group ID |
externalModifierOptionId | string | null | No | External platform option ID |
quantity | integer | No | Quantity (default 1) |
priceDelta | number | No | Price adjustment applied (default 0) |
Query options
| Resource | Filters | Sort fields |
|---|---|---|
ModifierGroup | search, locationId, isRequired, isActive | name, displayOrder, createdAt |
ModifierOption | search, locationId, modifierGroupId, isDefault, isActive | name, displayOrder, createdAt |
ItemModifierBinding | locationId, menuItemId, menuSetId, modifierGroupId, isActive | displayOrder, createdAt |
Define a group once and reuse it across items; use bindings (not new groups) for item-specific rules, excludedOptionIds to hide options that don't apply, and optionOverrides to re-price an option for a single dish.