> ## Documentation Index
> Fetch the complete documentation index at: https://spreecommerce.org/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Monetary amounts in the Admin API: prices, costs, formats

> How the Admin API represents money as decimal strings, why JSON numbers are avoided, and how to send prices, costs, and amounts safely in requests.

All monetary values in the Admin API are **canonical decimal strings** (e.g., `"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](#canonical-format-not-localized)).

## Reading

Every monetary field is returned as a string, alongside a `display_` companion that includes currency formatting:

```json theme={"theme":"night-owl"}
{
  "amount": "29.99",
  "display_amount": "$29.99",
  "compare_at_amount": "39.99",
  "display_compare_at_amount": "$39.99"
}
```

Use `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:

<CodeGroup>
  ```typescript Admin SDK theme={"theme":"night-owl"}
  await client.products.create({
    name: 'Classic Tee',
    prices: [{ currency: 'USD', amount: '29.99', compare_at_amount: '39.99' }],
  })
  ```

  ```bash CLI theme={"theme":"night-owl"}
  spree api post /products -d '{"name":"Classic Tee","prices":[{"currency":"USD","amount":"29.99","compare_at_amount":"39.99"}]}'
  ```
</CodeGroup>

JSON numbers are also accepted (`"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:

| Resource              | Monetary Fields                                                                                                                   |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **Product / Variant** | `price`, `compare_at_amount`, `cost_price`                                                                                        |
| **Price**             | `amount`, `compare_at_amount`                                                                                                     |
| **Order**             | `total`, `item_total`, `ship_total`, `tax_total`, `adjustment_total`, `promo_total`, `included_tax_total`, `additional_tax_total` |
| **Line Item**         | `price`, `total`, `adjustment_total`, `promo_total`, `pre_tax_amount`, `discounted_amount`, `compare_at_amount`                   |
| **Payment**           | `amount`                                                                                                                          |
| **Refund**            | `amount`                                                                                                                          |
| **Shipment**          | `cost`                                                                                                                            |
| **Gift Card**         | `amount`, `amount_used`, `amount_authorized`, `amount_remaining`                                                                  |
| **Store Credit**      | `amount`, `amount_used`, `amount_remaining`                                                                                       |

### 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:

| Resource                                                   | Money keys in `preferences`                          |
| ---------------------------------------------------------- | ---------------------------------------------------- |
| **Calculator** `flat_rate`, `per_item`, `digital_delivery` | `amount`                                             |
| **Calculator** `flexi_rate`                                | `first_item`, `additional_item`                      |
| **Calculator** `price_sack`                                | `minimal_amount`, `normal_amount`, `discount_amount` |
| **Calculator** `tiered_flat_rate`                          | `base_amount`, and the values of `tiers`             |
| **Calculator** `flat_rate` (shipping)                      | `amount`, `minimum_item_total`, `maximum_item_total` |
| **Promotion Rule** `item_total`                            | `amount_min`, `amount_max`                           |

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.
