---
title: Calculators
description: The small pieces of arithmetic behind delivery charges and promotion discounts — what each one does and how to choose between them.
---

## Overview

A calculator works out an amount. "Charge \$5 per item." "Take 15% off." "Free over \$50, otherwise \$7."

They exist because the rule and the number are different questions. A promotion decides *whether* a discount applies; a calculator decides *how much*. Keeping them apart means a merchant can change "10% off" to "$10 off" without touching the conditions that decide who qualifies.

Two things use calculators:

- **[Delivery methods](fulfillments.md)** — what delivery costs
- **[Promotions](promotions.md)** — how big a discount is

> **INFO:** Tax does **not** use calculators. Tax is worked out by a [tax provider](taxes.md), which can be Spree's own rate tables or an external service.

## Choosing a calculator

Each calculator takes a few settings — the amount, the percentage, the threshold. A merchant picks one and fills in the settings when setting up a delivery method or a promotion.

### For delivery charges

| Calculator | What it charges | Settings |
|---|---|---|
| **Flat rate** | The same amount every time | `amount`, `currency` |
| **Per item** | An amount for each item in the parcel | `amount`, `currency` |
| **Percent of item total** | A percentage of what's in the parcel | `flat_percent` |
| **Price sack** | One amount above a threshold, another below — the usual "free delivery over $50" | `minimal_amount`, `discount_amount`, `normal_amount` |
| **Flexi rate** | A charge for the first item, less for each one after | `first_item`, `additional_item`, `max_items` |
| **Digital delivery** | Nothing, for downloads | — |

### For promotion discounts

| Calculator | What it takes off | Settings |
|---|---|---|
| **Flat rate** | A fixed amount | `amount`, `currency` |
| **Percent of item total** | A percentage of the order | `flat_percent` |
| **Percent on line item** | A percentage of one item | `percent` |
| **Flexi rate** | A sliding amount by quantity | `first_item`, `additional_item`, `max_items` |
| **Tiered percent** | A percentage that grows with order value | tiers |
| **Tiered flat rate** | A fixed amount that grows with order value | tiers |

## Worked examples

<details>
<summary>Free delivery over \$50, otherwise \$7</summary>

**Price sack**, with `minimal_amount: 50`, `discount_amount: 0`, `normal_amount: 7`.

    A \$60 basket pays nothing. A \$20 basket pays \$7.

</details>

  <details>
<summary>\$3 to ship the first item, \$1 for each extra</summary>

**Flexi rate**, with `first_item: 3`, `additional_item: 1`.

    Four items cost `$3 + (3 × $1)` = **\$6**. Set `max_items` to stop charging beyond a point.

</details>

  <details>
<summary>10% off the order</summary>

**Percent of item total**, with `flat_percent: 10`.

    A \$31 order gets **\$3.10** off.

</details>

  <details>
<summary>Spend more, save more</summary>

**Tiered percent** — 5% over \$100, 10% over \$250, 15% over \$500.

    The order total picks the tier. One promotion covers the whole ladder instead of three competing ones.

</details>


## Setting one up

Calculators are configured as part of the thing that uses them. The available calculators for each are discoverable, so a dashboard or a script can present the real list rather than a hardcoded one:


```typescript Admin SDK
// What can price a delivery method?
const calculators = await adminClient.deliveryMethods.calculators()

// Set up a delivery method with a flat rate
await adminClient.deliveryMethods.create({
  name: 'Standard shipping',
  delivery_zone_id: 'dz_xxx',
  calculator_type: 'flat_rate',
  calculator_attributes: {
    preferences: { amount: '7.00', currency: 'USD' },
  },
})
```

```bash CLI
spree api get /delivery_methods/calculators
```

```bash cURL
curl 'https://api.mystore.com/api/v3/admin/delivery_methods/calculators' \
  -H 'X-Spree-API-Key: sk_xxx'
```


Each calculator declares which settings it takes, so a form can be built for it without knowing the calculator in advance — which is how the dashboard renders these.

> **WARNING:** A calculator with an `amount` also has a **currency**. If it doesn't match the order's currency, the calculator contributes nothing rather than converting. Set up one calculator per currency you sell in, or a promotion will silently do nothing for some customers.

## Writing your own

When none of the built-in calculators expresses your pricing — dimensional weight, a contract rate card, a rule your finance team invented — you can add one. A calculator is a small class with a name and a method that returns an amount, registered so it appears alongside the built-in options.

For delivery specifically, consider whether you want a calculator or a **delivery rate provider**. A calculator computes a number from what's in the parcel. A provider asks a carrier for real, live rates. If you want what UPS would actually charge today, that's a provider.

See [Custom promotions](../how-to/custom-promotion.md) and [Providers](../providers/overview.md).

## Related

- [Fulfillments](fulfillments.md) — delivery methods and rates
- [Promotions](promotions.md) — the rules that decide when a discount applies
- [Discounts](discounts.md) — the rows a promotion produces
- [Taxes](taxes.md) — worked out by providers, not calculators
