product.price_updated is a scoped event — unlike product.updated, it only carries prices. Every other product attribute (name, description, images, modifiers, etc.) is left untouched.
Supports multiple products in a single event. When the change affects multiple stores, Fire emits a single event with all affected stores listed in targets.
A modifier option’s price is contextual: it belongs to the combination (parent product × group × option), not to the option as a standalone product — the same option can be priced differently under two parents. That’s why it travels where it already lives in menu.updated: in products[].productModifiers[].overrides[], nested under the parent and scoped by the group’s modifierId. There is no separate array.
Payload
Fields
data
data.targets[n]
Same structure as in product.updated — includes vendorId and timezone.
data.targets[n].channels[n]
⚠️
listName in this event is the menu name (e.g. Menu App), while list.listName in
menu.updated is an auto-generated label (IFOOD - Store 1350). They are not the same value and
can’t be used to correlate — use listId, which is identical in both events.
data.products[n]
All four values come from the product’s item in the destination combination’s price list
(the same one identified by
listId), not from any default list.
products[] includes the parent product of each option with a contextual price, even if its own
price didn’t change: that way you always have the context of the product the change comes from. That
priceInfo is the parent’s current price, so applying it is an idempotent replace (you write back
the value it already has). If two edited options share the same parent, the parent appears only
once. A product with no price in the destination list is omitted.
⚠️ An option’s base price does NOT travel in this event. This screen edits the contextual
layer (the option’s price under a parent), never the option-product’s own price, so carrying the base
price would assert a price that didn’t change — and since priceInfo is a full replace, it could
overwrite the one you already have. The base price comes from the last
menu.updated, where the option travels as a product of type
MODIFIER, and it stays valid.
If what changed is a product’s own price and that product also happens to be an option elsewhere, it
arrives as a normal products[] entry: it’s a product edit, not an option edit.
⚠️ This priceInfo does not have the same fields as the priceInfo of products[n] in
menu.updated / product.updated
({pointPrice, price, referencePrice, suggestedPrice}). They’re deliberately different: this event
carries the sale price just saved along with its discount, not the product’s catalog prices.
data.products[n].productModifiers[n]
Same vocabulary as products[n].productModifiers[n] in
menu.updated.
Which groups arrive, and with what:
- Only groups that have some contextual price under that parent. A missing group means “its options use their base price,” not “no change”: contextual prices are only added or updated, never removed, so absence always reflects the actual state.
- Groups that do arrive bring all of their current contextual prices, not just the ones just
edited. That lets you merge by
modifierIdwithout losing the ones you already had. - Unlike
menu.updated, the group does not carryposition: order is structure, and this event only carries prices. Keep the order you already have from the menu.
modifierId × option’s productId. The
nesting already expresses it: applying the price by looking up only the option’s productId is
wrong, because the same option can appear under several parents, or under the same group assigned
twice to the same parent (two different modifierIds), with different prices in each case.
An override carries only price: an option’s contextual price has no discounted or suggested price
of its own — those concepts only exist at the product level, in products[n].priceInfo.
Combos: arrive reconstructed
A COMBO has no price of its own: its value is derived from the options of its required groups. That’s why it’s emitted just like inmenu.updated —
price: 0 and the actual value in referencePrice:
referencePrice= for every group withminOptions ≥ 1,minOptions × (lowest effective price among its options), summed across all those groups. Optional groups (minOptions = 0) don’t contribute.
minOptions doesn’t travel in this event — it’s a field of
data.menu.modifierGroups[n] in
menu.updated, correlated by modifierId. To recompute referencePrice you need both events: this
one gives you the current contextual prices, menu.updated gives you which groups are required.
Since changing a single option moves that value, when an option’s price changes on a combo the
event carries, for all groups with a current contextual price under this parent — not just the
edited group —, the full set of their overrides, so you can recompute the reference on your own and
validate it against the one we send. As with any product, a required group with no current override
does not appear in this event (see
“Which groups arrive”); for its minimum use the base price of those
options from menu.updated. Options with no resolvable price don’t participate in the minimum
calculation.
Example
position. Cross-referencing modifierId with minOptions from menu.updated:
⚠️ If this combo also had a required group whose options all kept their base price (no override at
all), that group wouldn’t appear in
productModifiers[] — that doesn’t mean it was removed, just
that there’s no contextual price to report. Its minimum comes from that modifierId’s options in
menu.updated.
⚠️ A group having overrides doesn’t mean it’s required: 7c2f9d4e83a1b650 carries contextual
prices for its options (they’re real add-ons, with their own price) but being optional
(minOptions = 0) it doesn’t participate in the sum.
On a regular product, options are add-ons and don’t change the parent’s price: the parent keeps its
list price and carries no referencePrice.
Behavior
This event only changes prices. Name, description, images, modifiers, taxes, and every other attribute remain unchanged.- For
products[n], the receivedpriceInfoobject fully replaces the existing one for thatproductId— it is not a field-by-field merge. - For
productModifiers[n].overrides[n],pricereplaces that option’s contextual price only under this parent and thismodifierId. Other occurrences of the same option are untouched. - An empty or absent array means “nothing to change here,” not “delete everything.”
- A product appearing in
products[]doesn’t mean its price changed: it may be there as the parent of an option (seedata.products[n]). The replace is idempotent in that case.
additionalInfo.assignedAt):
that value is membership data, not edit data. That’s why this event doesn’t carry it — the
assignedAt you already have on record for that product stays valid. It arrives in
product.updated and in
menu.updated.

