"29.99", "0.0" — period decimal, no thousands grouping), both in responses and in request bodies. Strings preserve decimal precision and avoid the floating-point rounding issues common with JSON numbers — critical when an admin is setting prices and costs. The format is the same regardless of locale; localized formatting is a presentation concern handled by the client (see Canonical format).
Reading
Every monetary field is returned as a string, alongside adisplay_ companion that includes currency formatting:
display_* for rendering and the raw string fields for calculations (parseFloat(price.amount)).
Writing
Send amounts back as strings too, so a value reads and writes in the same type:"amount": 29.99), but strings are recommended: they round-trip with what the API returns and sidestep float precision. A null clears the value.
Canonical format — not localized
Amounts are canonical: a period decimal separator and no thousands grouping ("1234.56"), independent of any locale. The Admin API does not parse locale-specific formats — do not send "1.234,56" or "1,234.56".
If you are taking input from a merchant in a localized format, normalize it to canonical form before sending. The Spree dashboard does this in the browser: a EUR price typed as 1.234,56 becomes 1234.56 on the wire. This matches how other commerce APIs handle money (canonical decimal or minor-unit integers; localization is a presentation concern).
Affected types
This convention applies to all monetary fields across all resources, including back-office-only fields the Store API never exposes:Amounts inside preferences
Calculators and some promotion rules carry their amounts inside an untyped preferences object (Record<string, unknown>) rather than as top-level fields. The same canonical-string convention applies to the money-valued keys within that hash — they read back as strings and accept canonical decimal strings on write. The keys that are money:
Percentage keys (
percent, flat_percent, base_percent), weights (minimum_weight, maximum_weight), and quantities (max_items, min_quantity) are not money — those stay numbers. Promotion actions (create_adjustment, create_item_adjustments) hold their money inside a nested calculator.preferences, per the calculator rows above.
