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

# Calculations

> Quote the tax for a cart at checkout

A calculation quotes the tax for a cart. Create one at checkout after the customer enters an address and before you take payment. Use its `id` to record a [transaction](https://docs.numeral.com/v2025-05-12/objects/transactions.md) once the customer pays.

The response has the totals and a breakdown for each line item. Most integrations charge `total_tax_amount` and keep the breakdown for their records.

```text Where calculations fit in checkout theme={null}
Checkout
├─ 1. Customer enters checkout
├─ 2. Customer enters address
│     └─ POST /tax/calculations
│          send:    products + address
│          receive: tax + calculation id
├─ 3. Tax added to the order total
└─ 4. Customer completes the purchase
      └─ POST /tax/transactions
           send:    calculation id
           receive: transaction id
```

Endpoints
[Create a calculationPOST/tax/calculations](#create-a-calculation)

## The Calculation object

### Attributes

**id** `string`

The ID of the calculation. Use it to create a transaction.

**object** `string, value is "tax.calculation"`

String representing the object's type.

**customer\_currency\_code** `string`

ISO 4217 currency code of the calculation.

**line\_items** `array of objects`

One entry per line item in the request.

**line\_items\[].line\_item\_id** `string`

Numeral-generated ID for this line item.

**line\_items\[].product** `object`

Product information for this line item.

**line\_items\[].product.reference\_line\_item\_id** `string`

Echo of the `reference_line_item_id` you sent. Empty when not sent.

**line\_items\[].product.reference\_product\_id** `string`

The product ID in your system. When the line fell back to `fallback_product_category`, a default product such as `default-general-merchandise`.

**line\_items\[].product.reference\_product\_name** `string`

The name of the product.

**line\_items\[].product.product\_tax\_code** `string`

The product category used to decide taxability.

**line\_items\[].tax\_jurisdictions** `array of objects`

One row per taxing jurisdiction that applies to this line item.

**line\_items\[].tax\_jurisdictions\[].tax\_rate** `number`

Statutory rate for the jurisdiction, as a decimal fraction (`0.0725` = 7.25%). Some rates apply to only part of the amount, so don't sum these. See [Effective tax rate](#effective-tax-rate).

**line\_items\[].tax\_jurisdictions\[].rate\_type** `string`

Type of rate: `sales_tax` for US Streamlined Sales Tax (SST) states, `general state sales tax` / `general county local sales tax` for other US states, `standard` for VAT countries. Zero-rate placeholder rows, e.g. when `automatic_tax` is `disabled`, use `VAT`.

**line\_items\[].tax\_jurisdictions\[].jurisdiction\_name** `string`

Name of the jurisdiction: a state or county name in the US, the ISO country code for VAT countries (e.g. `DE`).

**line\_items\[].tax\_jurisdictions\[].fee\_amount** `number`

Any fixed fee for this jurisdiction.

**line\_items\[].tax\_jurisdictions\[].note** `string`

How the row was calculated, e.g. `RATE_FRAC`. Zero-tax placeholder rows carry a reason instead, e.g. `automatic_tax_disabled`, or `no_collection_exempt_customer` for a [tax-exempt customer](https://docs.numeral.com/v2025-05-12/objects/customers.md#use-a-customer-in-a-calculation).

**line\_items\[].tax\_amount** `number`

Tax for this line item, in the currency's smallest unit.

**line\_items\[].amount\_excluding\_tax** `number`

Line amount excluding tax.

**line\_items\[].amount\_including\_tax** `number`

Line amount including tax.

**line\_items\[].quantity** `number`

Echo of the quantity you sent.

**total\_tax\_amount** `integer`

Total tax to charge, in the currency's smallest unit, rounded to an integer.

**total\_amount\_excluding\_tax** `integer`

Total excluding tax, in the currency's smallest unit, rounded to an integer.

**total\_amount\_including\_tax** `integer`

Total including tax, rounded to an integer. What you charge your customer.

**tax\_included\_in\_amount** `boolean`

Echo of the request: whether line item amounts already include tax.

**automatic\_tax** `enum`

`auto` or `disabled`. Echo of the request.

**customer** `object`

The customer as used for the calculation.

**customer.type** `enum`

`CONSUMER` (private individuals) or `BUSINESS` (companies or legal entities registered for VAT/GST).

**customer.tax\_ids** `array of objects`

Echo of the `tax_ids` you sent, when present. Each has `type` (`VAT`, `GST`, or `EIN`) and `value`.

**address\_resolution\_status** `enum`

How the customer address was resolved: `EXACT` (exact address match), `POSTAL_FALLBACK_1` (fell back to the postal code), or `POSTAL_ONLY` (only the postal code was used).

**address\_used** `object`

The address the tax was calculated for, after resolution.

**address\_used.address\_line\_1** `string`

**address\_used.address\_line\_2** `string`

Only present when there is one.

**address\_used.address\_city** `string`

**address\_used.address\_province** `string`

ISO 3166-2 subdivision code.

**address\_used.address\_postal\_code** `string`

**address\_used.address\_country** `string`

ISO 3166-1 alpha-2 country code.

**expires\_at** `timestamp`

Unix seconds when the quoted rates expire, 24 hours after creation.

**testmode** `boolean`

`true` if made with a test API key.

**metadata** `object`

Key-value pairs from the request. Only present when sent.

```json The Calculation object theme={null}
{
  "testmode": true,
  "id": "calc_1783369568468633ea55e-084a-419e-9d47-9f07aa27f571",
  "object": "tax.calculation",
  "customer_currency_code": "USD",
  "line_items": [
    {
      "line_item_id": "li_1783369568468ae179a21-411f-4a7f-a192-66a23331dc24",
      "product": {
        "reference_line_item_id": "",
        "reference_product_id": "wand_elder_12",
        "reference_product_name": "Elder Wand",
        "product_tax_code": "GENERAL_MERCHANDISE"
      },
      "tax_jurisdictions": [
        {
          "tax_rate": 0.0725,
          "rate_type": "general state sales tax",
          "jurisdiction_name": "California",
          "fee_amount": 0,
          "note": "RATE_FRAC"
        },
        {
          "tax_rate": 0.025,
          "rate_type": "general county local sales tax",
          "jurisdiction_name": "Los Angeles",
          "fee_amount": 0,
          "note": "RATE_FRAC"
        }
      ],
      "tax_amount": 975,
      "amount_excluding_tax": 10000,
      "amount_including_tax": 10975,
      "quantity": 1
    }
  ],
  "total_tax_amount": 975,
  "tax_included_in_amount": false,
  "total_amount_excluding_tax": 10000,
  "total_amount_including_tax": 10975,
  "expires_at": 1783455968,
  "customer": {
    "type": "CONSUMER"
  },
  "automatic_tax": "auto",
  "address_resolution_status": "EXACT",
  "address_used": {
    "address_line_1": "4 Privet Drive",
    "address_city": "Little Whinging",
    "address_province": "CA",
    "address_postal_code": "90210",
    "address_country": "US"
  }
}
```

## Effective tax rate

Each `tax_rate` is the statutory rate for its jurisdiction, and in some jurisdictions a rate applies to only part of the line item amount, so adding up `tax_rate` values can overstate the tax.

* Tennessee's single article rules are the common case: local rates apply only to the first \$1,600 of an item, and the 2.75% state single article rate only to the portion between \$1,600 and \$3,200. For a \$10,000 item the returned rates sum to 12.25%, but the tax due is \$788, an effective rate of 7.88%.
* Rows whose rate applies to only part of the amount carry a `note` other than `RATE_FRAC`, such as `PROGRESSIVE_RATE`.
* There's no effective rate field in this version. For a line, compute `tax_amount / amount_excluding_tax`.

Always charge the returned `tax_amount` (or `total_tax_amount`).

## Create a calculation

Quotes tax for a cart. Send `X-API-Version: 2025-05-12` on every request; without it, requests fall back to `2024-09-01` unless your account has a default version set. [Try it in the API reference](https://docs.numeral.com/api-reference/v2025-05-12/endpoint/calculations).

> [!WARNING]
>
> In this version, line item `amount` is the **total for the line**, not the per-unit price. `quantity` doesn't multiply it.

### Parameters

**customer** `object` *required*

Who is buying. `customer.address` is required.

**customer.address** `object` *required*

Destination address. This version supports 82 destination countries: US, Canada, the EU member states, and more (full list in the [API reference](https://docs.numeral.com/api-reference/v2025-05-12/endpoint/calculations)).

**customer.address.address\_line\_1** `string` *required*

**customer.address.address\_line\_2** `string`

**customer.address.address\_city** `string` *required*

**customer.address.address\_province** `string` *required*

ISO 3166-2 subdivision code. For US and Canada, a valid uppercase USPS state code or Canadian province code.

**customer.address.address\_postal\_code** `string` *required*

Required for US and Canada.

**customer.address.address\_country** `string` *required*

ISO 3166-1 alpha-2 country code.

**customer.address.address\_type** `enum` *required*

`shipping` for physical goods, `billing` for software and digital goods. Only valid on `customer.address`.

**customer.id** `string`

The customer's `id` or your `reference_customer_id`, for a [customer](https://docs.numeral.com/v2025-05-12/objects/customers.md) you created in Numeral. Used for record keeping and tax exemptions.

**customer.type** `enum` (default: CONSUMER)

`CONSUMER` for private individuals not registered for VAT/GST. `BUSINESS` for companies, sole proprietors, or other legal entities registered for VAT/GST. See [Business customers and tax IDs](#business-customers-and-tax-ids).

**customer.tax\_ids** `array of objects`

Required when `type` is `BUSINESS` and the address is outside the US. Not allowed for `CONSUMER`. Each has `type` (`VAT`, `GST`, or `EIN`) and `value`, both required.

**customer.skip\_tax\_id\_validation** `boolean` (default: false)

Skip format and registry checks on `tax_ids`.

**order\_details** `object` *required*

The cart.

**order\_details.customer\_currency\_code** `string` *required*

ISO 4217 currency code. This version supports 126 currencies. See [supported currencies](https://docs.numeral.com/essentials/currencies.md).

**order\_details.tax\_included\_in\_amount** `boolean` *required*

Whether line item amounts already include tax. Usually `false` in North America.

**order\_details.automatic\_tax** `enum` *required*

`auto` returns real rates where you have an active registration. `disabled` always returns 0. Must be `auto` for US addresses. See [Automatic tax](#automatic-tax).

**order\_details.line\_items** `array of objects` *required*

One entry per product. Each needs `reference_product_id` or `product_category`.

**order\_details.line\_items\[].amount** `integer` *required*

**Total** price of the line, not per-unit, in the currency's smallest unit (`2000` = \$20.00 in USD, ¥2000 in JPY). Tax is calculated on this value as is.

**order\_details.line\_items\[].quantity** `integer` *required*

Quantity sold. Record keeping only in this version: it doesn't change the taxable amount.

**order\_details.line\_items\[].product\_category** `string`

Tax category from the [category list](https://docs.numeral.com/essentials/product-categories.md). Required if no `reference_product_id`.

**order\_details.line\_items\[].reference\_product\_id** `string`

Your [product](https://docs.numeral.com/v2025-05-12/objects/products.md) ID, as created in Numeral. Required if no `product_category`.

**order\_details.line\_items\[].fallback\_product\_category** `string`

Used when `reference_product_id` isn't found in Numeral, instead of returning an error. Must be a valid category, or the request returns a 400. If the product is found, its stored category wins.

**order\_details.line\_items\[].reference\_line\_item\_id** `string`

Your line item ID, echoed back for record keeping.

**origin\_address** `object`

Ship-from address. Required unless a default origin address is set for the destination jurisdiction on your account; without either, the request returns a 400. Same fields as `customer.address`, without `address_type`.

**metadata** `object`

Up to 50 keys, each up to 255 characters. Values must be strings of up to 255 characters.

### Returns

A [Calculation object](#the-calculation-object).

```bash POST /tax/calculations theme={null}
curl https://api.numeralhq.com/tax/calculations \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Version: 2025-05-12' \
  -d '{
    "customer": {
      "type": "CONSUMER",
      "address": {
        "address_line_1": "4 Privet Drive",
        "address_city": "Little Whinging",
        "address_province": "CA",
        "address_postal_code": "90210",
        "address_country": "US",
        "address_type": "shipping"
      }
    },
    "origin_address": {
      "address_line_1": "3990 N County Rd 300 E",
      "address_city": "Danville",
      "address_province": "IN",
      "address_postal_code": "46122",
      "address_country": "US"
    },
    "order_details": {
      "customer_currency_code": "USD",
      "tax_included_in_amount": false,
      "automatic_tax": "auto",
      "line_items": [
        {
          "reference_product_id": "wand_elder_12",
          "amount": 10000,
          "quantity": 1
        }
      ]
    }
  }'
```

```json Response theme={null}
{
  "id": "calc_1783369568468633ea55e-084a-419e-9d47-9f07aa27f571",
  "object": "tax.calculation",
  "total_tax_amount": 975,
  "total_amount_excluding_tax": 10000,
  "total_amount_including_tax": 10975,
  "...": "see The Calculation object"
}
```

## Business customers and tax IDs

`customer.type` switches on business-to-business (B2B) tax logic.

* `CONSUMER` (default): standard business-to-consumer tax. `tax_ids` aren't allowed.
* `BUSINESS`: send valid `tax_ids` (not needed for US addresses). This enables B2B exemptions and reverse charge where they apply.

Tax IDs are validated in real time, unless you send `skip_tax_id_validation: true`:

* `VAT` IDs are checked against the EU VIES database.
* `GST` IDs are validated for the countries where they apply.
* An invalid tax ID returns an error.

This version accepts only the `VAT`, `GST`, and `EIN` tax ID types.

```bash Business request theme={null}
curl https://api.numeralhq.com/tax/calculations \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Version: 2025-05-12' \
  -d '{
    "customer": {
      "type": "BUSINESS",
      "tax_ids": [
        { "type": "VAT", "value": "GB123456789" }
      ],
      "address": {
        "address_line_1": "123 Business St",
        "address_city": "London",
        "address_province": "England",
        "address_postal_code": "EC1A 1BB",
        "address_country": "GB",
        "address_type": "billing"
      }
    },
    "origin_address": {
      "address_line_1": "Tech Park 1",
      "address_city": "Dublin",
      "address_province": "L",
      "address_postal_code": "D02 XY45",
      "address_country": "IE"
    },
    "order_details": {
      "customer_currency_code": "EUR",
      "tax_included_in_amount": false,
      "automatic_tax": "auto",
      "line_items": [
        {
          "reference_product_id": "business_software_license",
          "amount": 50000,
          "quantity": 5
        }
      ]
    }
  }'
```

```json Business response theme={null}
{
  "testmode": true,
  "id": "calc_1783369568468633ea55e-084a-419e-9d47-9f07aa27f572",
  "object": "tax.calculation",
  "customer_currency_code": "EUR",
  "line_items": [
    {
      "line_item_id": "li_1783369568468ae179a21-411f-4a7f-a192-66a23331dc25",
      "product": {
        "reference_line_item_id": "",
        "reference_product_id": "business_software_license",
        "reference_product_name": "Business Software License",
        "product_tax_code": "SAAS_GENERAL"
      },
      "tax_jurisdictions": [
        {
          "tax_rate": 0.2,
          "rate_type": "standard",
          "jurisdiction_name": "GB",
          "fee_amount": 0,
          "note": "RATE_FRAC"
        }
      ],
      "tax_amount": 10000,
      "amount_excluding_tax": 50000,
      "amount_including_tax": 60000,
      "quantity": 5
    }
  ],
  "total_tax_amount": 10000,
  "tax_included_in_amount": false,
  "total_amount_excluding_tax": 50000,
  "total_amount_including_tax": 60000,
  "expires_at": 1783455968,
  "customer": {
    "type": "BUSINESS",
    "tax_ids": [
      { "type": "VAT", "value": "GB123456789" }
    ]
  },
  "automatic_tax": "auto",
  "address_resolution_status": "POSTAL_ONLY",
  "address_used": {
    "address_line_1": "123 Business St",
    "address_city": "London",
    "address_province": "England",
    "address_postal_code": "EC1A 1BB",
    "address_country": "GB"
  }
}
```

## Automatic tax

`order_details.automatic_tax` controls whether tax is collected. Neither setting registers you anywhere.

| `automatic_tax` | Tax rates returned | Use case |
| - | - | - |
| `auto` | Where you have an active registration | Standard operation. Required for US addresses. |
| `disabled` | Always 0 | Tax-exempt scenarios |

With `disabled`, line items get zero-rate placeholder rows with `rate_type: "VAT"` and `note: "automatic_tax_disabled"`.

## Errors

Errors return an `error` object with `error_code`, `error_message`, and sometimes `error_meta` (details such as the offending `field` and the `expected` and `received` types). Handle them by `error_code`. The request ID is in the `X-Request-Id` response header.

| HTTP | `error_code` | When |
| - | - | - |
| 400 | `missing_field` | A required field is missing. |
| 400 | `incorrect_type` | A field has the wrong type. |
| 400 | `unrecognized_field` | An unknown field was sent, e.g. `address_type` on `origin_address`. |
| 400 | `malformed_address` | The address is invalid or incomplete. |
| 400 | `invalid_currency_code` | The currency isn't supported. |

Also returned:

* 400 when there's no `origin_address` and no default origin address for the destination.
* 400 when `fallback_product_category` isn't a valid category.
* 404 when `reference_product_id` doesn't exist and no `fallback_product_category` was sent.

```json 400 theme={null}
{
  "error": {
    "error_code": "missing_field",
    "error_message": "Field is required"
  }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.