> Documentation index: [Saleor](/llms.txt) · [This section](/developer/llms.txt)
> Source: https://docs.saleor.io/developer/taxes

# Taxes

Saleor offers two ways of calculating taxes:

-   **flat rates** (simple, static rates)
-   **dynamic taxes** (real-time calculations from external providers).

You can configure the tax calculation method per sales channel and override it for specific countries.

<a id="tax-configuration"></a>

## Tax Configuration

Tax configuration controls how taxes are calculated and displayed in your store. Each sales channel has its own tax configuration that determines:

-   Whether taxes are charged in a channel
-   Which calculation method to use (flat rates or tax app)
-   Whether prices include taxes
-   How prices are displayed in your storefront

<a id="where-to-configure-taxes"></a>

### Where to Configure Taxes

You can configure taxes in two ways:

**In Saleor Dashboard:**

1.  Go to **Configuration → Taxes**
2.  Select the channel you want to configure
3.  Adjust the tax settings for that channel

**Via API:** Use the [`taxConfigurationUpdate`](/api-reference/taxes/mutations/tax-configuration-update.md) mutation to update tax settings programmatically.

<a id="key-configuration-settings"></a>

### Key Configuration Settings

-   **Charge taxes** - When enabled, customers pay prices including tax. When disabled, taxes are still calculated and available for display, but customers pay the net prices.
-   **Tax calculation method** - Choose between flat rates (default) or dynamic taxes (tax apps).
-   **Display gross prices** - Controls whether your storefront shows prices with tax included. This only affects display, not calculations.
-   **Prices entered with tax** - Indicates whether your product prices include tax (common in EU B2C) or exclude tax (common in US and B2B).
-   **Use weighted tax for shipping** - When enabled, shipping tax is calculated as a weighted average of product tax rates. Available only when using the `FLAT_RATES` tax calculation strategy (from Saleor 3.21).

<a id="tax-classes"></a>

### Tax classes

The Tax Class is the most critical configuration setting for accurate tax calculation. They allow you to:

-   For **Flat Rates**: Override default country rates for specific items (e.g., reduced rates for books).
-   For **Dynamic Taxes**: Map products or shipping methods to specific provider tax codes (e.g., AvaTax Tax Codes).

<a id="how-to-create-tax-classes"></a>

#### How to create tax classes:

**In Dashboard:**

1.  Go to **Configuration → Taxes**
2.  Click on **Tax Classes** tab
3.  Click **Add tax class** or edit an existing one
4.  Enter a name for the tax class (e.g., "Healthcare products")
5.  Add tax rates for each country where you sell
6.  Save the tax class
7.  Assign the tax class to products, product types or shipping method:
    -   For individual products: Go to **Catalog → Products**, select a product, and assign the tax class
    -   For product types: Go to **Configuration → Product Types**, select a product type, and assign the tax class
    -   For shipping methods: Go to **Configuration -> Shipping Methods**, select shipping zone, select rate, and assign the tax class

**Via API:**

Create a tax class:

**Mutation**

```graphql
mutation TaxClassCreate($input: TaxClassCreateInput!) {
  taxClassCreate(input: $input) {
    errors {
      field
      message
    }
    taxClass {
      id
      name
      countries {
        rate
      }
    }
  }
}
```

**Variables**

```json
{
  "input": {
    "name": "Healthcare products",
    "createCountryRates": [
      {
        "countryCode": "PL",
        "rate": 8
      }
    ]
  }
}
```

Assign the tax class to a product:

**Mutation**

```graphql
mutation ProductUpdate($id: ID, $input: ProductInput!) {
  productUpdate(id: $id, input: $input) {
    errors {
      field
      message
    }
    product {
      taxClass {
        name
      }
    }
  }
}
```

**Variables**

```json
{
  "id": "UHJvZHVjdDox",
  "input": {
    "taxClass": "VGF4Q2xhc3M6Mg=="
  }
}
```

Assign the tax class to a product type:

**Mutation**

```graphql
mutation ProductTypeUpdate($id: ID!, $input: ProductTypeInput!) {
  productTypeUpdate(id: $id, input: $input) {
    errors {
      field
      message
    }
    productType {
      taxClass {
        name
      }
    }
  }
}
```

**Variables**

```json
{
  "id": "UHJvZHVjdFR5cGU6MQ==",
  "input": {
    "taxClass": "VGF4Q2xhc3M6Mg=="
  }
}
```

Assign the tax class to a shipping method:

**Mutation**

```graphql
mutation UpdateShippingRate($id: ID!, $input: ShippingPriceInput!) {
  shippingPriceUpdate(id: $id, input: $input) {
    errors {
      field
      code
      message
    }
    shippingMethod {
      taxClass{
        name
      }
    }
  }
}
```

**Variables**

```json
{
  "id": "U2hpcHBpbmdNZXRob2RUeXBlOjM=",
  "input": {
    "taxClass": "VGF4Q2xhc3M6Mg=="
  }
}
```

<a id="fetching-tax-configurations"></a>

### Fetching Tax Configurations

To view your current tax configuration use query:

**Query**

```graphql
query TaxConfigurations {
  taxConfigurations(first: 10) {
    edges {
      node {
        id
        channel {
          slug
        }
        chargeTaxes
        displayGrossPrices
        pricesEnteredWithTax
        taxCalculationStrategy
        taxAppId
      }
    }
  }
}
```

<a id="which-address-is-used-for-tax-calculation"></a>

## Which Address is Used for Tax Calculation?

Saleor determines which address to use for tax calculation in this order of priority:

1.  **Shipping address** (if available)
2.  **Billing address** (if shipping address is not available)
3.  **Channel's default country** (if neither address is available)

This means:

-   For shippable products, taxes are calculated based on where the product is being shipped
-   For digital products, taxes are calculated based on the billing address
-   If no address is provided, taxes use the channel's default country

**Example scenarios:**

_sShippable products:_\*

-   Customer adds items to cart → No address yet → Uses channel default country
-   Customer enters billing address → Uses billing address for tax calculation
-   Customer enters shipping address → Now uses shipping address for tax calculation

**Digital products:**

-   Customer adds digital items → No address yet → Uses channel default country
-   Customer enters billing address → Uses billing address for tax calculation

**Click and collect:**

-   Customer adds items to cart → No address yet → Uses channel default country
-   Customer enters billing address → Uses billing address for tax calculation
-   Customer enters shipping address → Now uses shipping address for tax calculation
-   Customer selects a collection point, the warehouse address becomes the shipping address and is used for tax calculation

<a id="flat-rates"></a>

## Flat Rates

Flat rates are the default tax calculation method in Saleor. They use static, manually configured tax rates that you set up for each country. This method is ideal for stores that:

-   Operate in a limited number of countries
-   Have straightforward tax requirements
-   Want full control over tax rates
-   Don't need real-time tax rate updates

<a id="business-use-cases"></a>

### Business Use Cases

Flat rates work well for:

-   **Stores with simple tax structures** (e.g., single VAT rate per country)
-   **Stores operating in regions** with stable tax rates

<a id="how-to-configure-flat-rates"></a>

### How to Configure Flat Rates

<a id="step-1-enable-flat-rates-for-your-channel"></a>

#### Step 1: Enable Flat Rates for Your Channel

**In Dashboard:**

1.  Go to **Configuration → Taxes**
2.  Select your **channel**
3.  Set **Method of tax calculation** to "Use flat tax rate"
4.  Save

**Via API:**

**Mutation**

```graphql
mutation TaxConfigurationUpdate($id: ID!, $input: TaxConfigurationUpdateInput!) {
  taxConfigurationUpdate(id: $id, input: $input) {
    errors {
      field
      message
    }
    taxConfiguration {
      taxCalculationStrategy
    }
  }
}
```

**Variables**

```json
{
  "id": "VGF4Q29uZmlndXJhdGlvbjox",
  "input": {
    "taxCalculationStrategy": "FLAT_RATES"
  }
}
```

<a id="step-2-set-default-country-rates"></a>

#### Step 2: Set Default Country Rates

Set default tax rates for each country where you sell. These rates apply to all products that don't have a specific tax class.

**In Dashboard:**

1.  Go to **Configuration → Taxes**
2.  Select **Countries** tab
3.  Click Add country or edit an existing one
4.  Enter the tax rate percentage (e.g., 23 for 23%)
5.  Save

**Via API:**

Use the `taxCountryConfigurationUpdate` mutation to set default country rates:

**Mutation**

```graphql
mutation TaxCountryConfigurationUpdate($countryCode: CountryCode!, $updateTaxClassRates: [TaxClassRateInput!]!) {
  taxCountryConfigurationUpdate(
    countryCode: $countryCode
    updateTaxClassRates: $updateTaxClassRates
  ) {
    taxCountryConfiguration {
      country {
        code
      }
      taxClassCountryRates {
        rate
      }
    }
    errors {
      field
      message
    }
  }
}
```

**Variables**

```json
{
  "countryCode": "PL",
  "updateTaxClassRates": [
    {
      "rate": 23
    }
  ]
}
```

This example sets a 23% default tax rate for Poland.

<a id="step-3-create-tax-classes-optional"></a>

#### Step 3: Create Tax Classes (Optional)

Tax classes are needed when some products require different tax rates than the default country rate. For example, if you sell healthcare products in Poland that are taxed at 8% instead of the standard 23%, you need a tax class for them. If all your products use the same tax rate, you can skip this step.

See [Tax class configuration](#tax-classes).

<a id="example-calculations"></a>

### Example Calculations

Here are practical examples of how flat rates calculate taxes:

**Example 1: Simple product with default rate**

-   Product price: $100.00
-   Default country rate (Poland): 23%
-   **Tax amount:** $100 × 0.23 = $23.00
-   **Total price:** $123.00

**Example 2: Product with tax class**

-   Product price: $100.00
-   Tax class rate (Healthcare, Poland): 8%
-   **Tax amount:** $100 × 0.08 = $8.00
-   **Total price:** $108.00

**Example 3: Multiple products with different rates**

-   Product A (standard): $100.00 at 23% = $23.00 tax
-   Product B (healthcare): $50.00 at 8% = $4.00 tax
-   **Total tax:** $27.00
-   **Total price:** $177.00

<a id="shipping-tax-rates"></a>

### Shipping Tax Rates

You can configure shipping tax rates in two ways:

1.  **Static rate** - Assign a tax class to the shipping method
2.  **Weighted rate** - Calculate shipping tax as a weighted average of product tax rates (available from Saleor 3.21 and **only** when using the `FLAT_RATES` tax calculation strategy)

<a id="enabling-weighted-shipping-tax-api"></a>

### Enabling Weighted Shipping Tax (API)

<a id="enable-for-the-whole-channel"></a>

#### Enable for the Whole Channel

Use `taxConfigurationUpdate` to enable weighted shipping tax for a channel:

**Mutation**

```graphql
mutation TaxConfigurationUpdate($id: ID!, $input: TaxConfigurationUpdateInput!) {
  taxConfigurationUpdate(id: $id, input: $input) {
    taxConfiguration {
      useWeightedTaxForShipping
      taxCalculationStrategy
    }
    errors {
      field
      message
    }
  }
}
```

**Variables**

```json
{
  "id": "VGF4Q29uZmlndXJhdGlvbjox",
  "input": {
    "taxCalculationStrategy": "FLAT_RATES",
    "useWeightedTaxForShipping": true
  }
}
```

<a id="enable-for-a-specific-country"></a>

#### Enable for a Specific Country

You can also enable weighted shipping tax only for selected countries within a channel:

**Mutation**

```graphql
mutation TaxConfigurationUpdate($id: ID!, $input: TaxConfigurationUpdateInput!) {
  taxConfigurationUpdate(id: $id, input: $input) {
    taxConfiguration {
      countries {
        country {
          code
        }
        useWeightedTaxForShipping
        taxCalculationStrategy
      }
    }
    errors {
      field
      message
    }
  }
}
```

**Variables**

```json
{
  "id": "VGF4Q29uZmlndXJhdGlvbjox",
  "input": {
    "updateCountriesConfiguration": [
      {
        "countryCode": "CA",
        "taxCalculationStrategy": "FLAT_RATES",
        "useWeightedTaxForShipping": true,
        "chargeTaxes": true,
        "displayGrossPrices": true
      }
    ]
  }
}
```

For weighted shipping tax, Saleor calculates:

```text
Weighted rate = (Σ line_net_price × line_tax_rate) / (Σ line_net_price)
```

**Example:**

-   Product 1: $100 at 23% tax
-   Product 2: $100 at 8% tax
-   Shipping: $20
-   **Weighted shipping tax rate:** (100×0.23 + 100×0.08) / (100 + 100) = 15.5%
-   **Shipping tax:** $20 × 0.155 = $3.10

<a id="dynamic-taxes"></a>

## Dynamic Taxes

Dynamic taxes delegate tax calculations to external tax providers like AvaTax, TaxJar, or custom tax apps. Instead of manually setting static rates, these providers calculate taxes in real-time based on current tax rules, product types, customer locations, and other factors.

<a id="business-use-cases-1"></a>

### Business Use Cases

Dynamic taxes are ideal for:

-   **Stores with complex tax requirements** (city, county, state taxes)
-   **Stores requiring tax compliance features** (reporting, filing)
-   **Stores selling to businesses** that need tax exemption handling

<a id="what-needs-to-be-configured"></a>

### What Needs to Be Configured

To use dynamic taxes, you need to:

1.  **Install a tax app** - Install a tax provider app from the Saleor App Store (e.g., AvaTax)
    
2.  **Configure the tax app** - Set up credentials and settings in the app
    
3.  **Enable dynamic taxes for channels** - Switch your channel's tax calculation method to use the tax app:
    
    **In Dashboard:**
    
    1.  Go to **Configuration → Taxes**
    2.  Select your **channel**
    3.  Set **Method of tax calculation** to "Use app **Tax app name**"
    4.  Save
    
    **Via API:**
    
    **Mutation**
    
    ```graphql
    mutation TaxConfigurationUpdate($id: ID!, $input: TaxConfigurationUpdateInput!) {
      taxConfigurationUpdate(id: $id, input: $input) {
        errors {
          field
          message
        }
        taxConfiguration {
          taxCalculationStrategy
          taxAppId
        }
      }
    }
    ```
    
    **Variables**
    
    ```json
    {
      "id": "VGF4Q29uZmlndXJhdGlvbjox",
      "input": {
        "taxCalculationStrategy": "TAX_APP",
        "taxAppId": "saleor.tax"
      }
    }
    ```
    
4.  **Create and assign tax classes to products, map tax classes in Tax App into tax provider codes** - Every product must have a tax class that maps to the provider's tax codes
    

For detailed AvaTax configuration instructions, see the [AvaTax App Configuration guide](/developer/app-store/apps/avatax/configuration.md).

To see available tax apps, use query:

```graphql
query GetShop {
  shop {
    availableTaxApps {
      id
      name
      identifier
    }
  }
}
```

<a id="country-specific-configuration-in-channel"></a>

## Country-Specific Configuration in Channel

You can use different tax calculation methods for different countries within the same channel. For example, use flat rates for EU countries and dynamic taxes for the US:

**In Dashboard:**

1.  Go to **Configuration → Taxes**
2.  Select your **channel**
3.  Scroll down to the **Country exceptions** section
4.  Click **Add country** or edit an existing country configuration
5.  Select the country you want to configure
6.  Set the **Method of tax calculation** for that country (flat rates or a specific tax app)
7.  Configure other tax settings for that country if needed (charge taxes, display gross prices, etc.)
8.  Save the configuration
9.  Repeat for each country that needs a different tax calculation method

**Via API:**

**Mutation**

```graphql
mutation TaxConfigurationUpdate($id: ID!, $input: TaxConfigurationUpdateInput!) {
  taxConfigurationUpdate(id: $id, input: $input) {
    errors {
      field
      message
    }
    taxConfiguration {
      taxCalculationStrategy
      countries {
        country {
          code
        }
        taxCalculationStrategy
      }
    }
  }
}
```

**Variables**

```json
{
  "id": "VGF4Q29uZmlndXJhdGlvbjox",
  "input": {
    "taxCalculationStrategy": "FLAT_RATES",
    "updateCountriesConfiguration": [
      {
        "countryCode": "US",
        "taxCalculationStrategy": "TAX_APP",
        "taxAppId": "saleor.tax"
      }
    ]
  }
}
```

<a id="rounding-rules"></a>

## Rounding Rules

Saleor uses standard rounding rules for tax calculations:

-   If the discarded fraction is >= 0.5, round up
-   If the discarded fraction is < 0.5, round down

**Examples:**

-   $9.945 → $9.95
-   $9.94499 → $9.94

Most currencies are calculated to 2 decimal places.

<a id="tax-exemption"></a>

## Tax Exemption

You can exempt specific checkouts or orders from taxes using the [Tax Exemption API](/api-reference/taxes/mutations/tax-exemption-manage.md). This is useful for tax-exempt customers or special cases.

**Mutation**

```graphql
mutation TaxExemptionManage($id: ID!, $taxExemption: Boolean!) {
  taxExemptionManage(id: $id, taxExemption: $taxExemption) {
    errors {
      field
      message
    }
    taxableObject {
      ... on Checkout {
        taxExemption
      }
    }
  }
}
```

**Variables**

```json
{
  "id": "Q2hlY2tvdXQ6ZTZiMzAzYmMtMzc2Zi00YThkLTkxYzAtNmU3MjYyNDU1OWU3",
  "taxExemption": true
}
```

note

Tax exemption requires the `MANAGE_TAXES` permission.
