4 MIN READ

Tax in Medusa: Regions, Rates, Overrides and When to Use a Tax Engine

Tax-inclusive versus tax-exclusive pricing, product overrides, and the point at which manual rates stop being viable and you buy an engine.

BY SHUBHAM VERMAUPDATED
Illustration for “Tax in Medusa: Regions, Rates, Overrides and When to Use a Tax Engine” — Tax

Tax is where commerce projects quietly acquire liability. The mechanics in Medusa are not complicated; the judgement about when manual rates stop being adequate is, and getting it wrong is expensive in a way that bugs usually are not.

The model

ConceptWhat it does
Tax regionA country, optionally with province sub-regions
Tax rateA percentage within a region; one default plus overrides
Tax overrideA rate applied to specific products or types
Tax providerThe module computing tax — built-in or external

Tax regions are separate from commerce regions. A commerce region groups countries by currency and payment methods; a tax region maps to a jurisdiction. They often overlap and are not the same thing.

Setting rates

src/scripts/setup-tax.tsts
import { ExecArgs } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"

export default async function setupTax({ container }: ExecArgs) {
  const tax = container.resolve(Modules.TAX)

  const [uk] = await tax.createTaxRegions([
    {
      country_code: "gb",
      default_tax_rate: { name: "UK VAT", code: "vat-standard", rate: 20 },
    },
  ])

  // Zero-rated categories are overrides on the region's default.
  await tax.createTaxRates([
    {
      tax_region_id: uk.id,
      name: "Zero rated",
      code: "vat-zero",
      rate: 0,
      rules: [{ reference: "product_type", reference_id: "books" }],
    },
  ])
}

Rates are percentages, so 20 means 20%. Overrides attach through rules referencing a product, product type or collection — which is how a store selling both standard and zero-rated goods stays correct without conditionals in the storefront.

Inclusive versus exclusive

The decision with the widest blast radius.

Tax-inclusive — the displayed price includes tax. Expected in the UK, EU and most consumer markets outside North America. A £120 product is £120 at checkout; £20 of it is VAT.

Tax-exclusive — tax is added at checkout. Standard in the US, and standard for B2B nearly everywhere.

Set it per region:

ts
await regionService.updateRegions(regionId, { automatic_taxes: true })
// and on the price list / region: is_tax_inclusive

Two things follow. Price entry changes — merchandisers enter gross prices for inclusive regions and net prices for exclusive ones, and mixing them up is a margin error nobody catches for weeks. And the same product sold inclusive in the EU and exclusive in the US needs separate price entries per region, not one price with tax bolted on.

For B2B, exclusive display with a VAT number field is the expectation. See B2B commerce.

When manual rates stop working

Manual rates are fine when:

  • You sell in one or two countries.
  • Rates are stable and few.
  • You do not cross the US state-nexus problem.

They stop working when:

  • You sell into the US. There are thousands of taxing jurisdictions, rates change constantly, and economic nexus rules mean you may owe tax in states you have never visited. Manual rates here are not a shortcut; they are a liability.
  • You sell cross-border in the EU. OSS thresholds mean the destination country's rate applies past a certain volume, per country.
  • Your categories have exemptions. Food, clothing, medical and digital goods each vary by jurisdiction.

At that point you integrate a tax engine — Avalara, TaxJar, Stripe Tax or similar. The provider interface makes it a module:

medusa-config.tsts
{
  resolve: "@medusajs/medusa/tax",
  options: {
    providers: [
      {
        resolve: "./src/modules/tax-avalara",
        id: "avalara",
        options: {
          accountId: process.env.AVALARA_ACCOUNT_ID,
          licenseKey: process.env.AVALARA_LICENSE_KEY,
          companyCode: process.env.AVALARA_COMPANY_CODE,
        },
      },
    ],
  },
}

The provider computes tax per line at checkout and, in most cases, files returns for you. The cost — typically a few hundred dollars a month — is trivial against the exposure of getting US sales tax wrong for a year.

Digital goods

Digital services are taxed where the customer is, not where you are, in most jurisdictions — including the EU and UK. That means:

  • Collect and retain evidence of customer location.
  • Apply the destination rate.
  • Register for the relevant scheme, such as EU OSS.

If you sell digital products internationally, treat this as a tax engine requirement from day one.

Practical guidance

Decide inclusive versus exclusive before entering a single price. Changing it later means re-entering the catalog.

Store the tax breakdown on the order. You will need line-level tax for returns, reporting and audits, and reconstructing it later from rates that have since changed is unpleasant.

Test refunds. Tax must be refunded proportionally. This is easy to get wrong on partial returns and very visible when you do.

Get advice. This post is orientation, not tax advice. Rates and rules change, and the consequences of guessing are financial rather than technical.

Reverse charge and B2B

Selling to a VAT-registered business in another EU country usually means reverse charge: you charge no VAT and the customer accounts for it themselves. That requires three things your checkout must do:

  1. Collect the VAT number at checkout for business customers.
  2. Validate it against the EU's VIES service. An invalid number means you charge VAT, and an unvalidated one leaves you liable.
  3. Record the validation with a timestamp. If you are audited, "the customer told us" is not evidence.
src/workflows/steps/validate-vat-number.tsts
export const validateVatNumberStep = createStep(
  "validate-vat-number",
  async ({ vat_number, country_code }: Input, { container }) => {
    const result = await checkVies(country_code, vat_number)

    return new StepResponse({
      valid: result.valid,
      // Store the evidence, not just the outcome.
      checked_at: result.requestDate,
      consultation_number: result.consultationNumber,
    })
  },
)

Then set the cart to tax-exclusive with a zero rate and note the reverse charge on the invoice. See B2B commerce.

Tax on shipping and discounts

Two details that are easy to get wrong and visible when you do:

Shipping is usually taxable, at the rate of the goods being shipped — and where an order contains items at different rates, many jurisdictions require apportioning shipping tax across them proportionally. A single blended rate is an approximation your accountant may not accept.

Discounts reduce the taxable base. A 20% discount on a €100 item means tax is calculated on €80, not €100. This falls out correctly if you apply promotions before tax calculation and incorrectly if you do it the other way round — worth an explicit test, because the error is small per order and systematic.


We wire tax engines and check the maths against real orders. Ask.

Frequently asked questions

How does tax work in Medusa v2?

Tax regions map to jurisdictions and hold a default rate plus overrides for specific products or types. At checkout the tax provider — built-in or external — computes tax per line item using the customer's address.

What is the difference between tax-inclusive and tax-exclusive pricing?

Inclusive prices contain the tax, which is the norm in the UK and EU consumer markets. Exclusive prices add tax at checkout, standard in the US and for B2B. It is set per region and determines whether merchandisers enter gross or net prices.

Do I need a tax engine like Avalara or TaxJar?

Not for one or two countries with stable rates. Yes if you sell into the US, where thousands of jurisdictions and economic nexus rules apply, or cross-border in the EU where OSS thresholds change which country's rate you charge.

How do I set a different tax rate for specific products?

Create a tax rate within the region with a rule referencing the product, product type or collection. That is how reduced and zero-rated categories such as books, food or children's clothing are handled.

How is tax handled for digital products?

In most jurisdictions digital services are taxed where the customer is located, which means collecting evidence of location, applying the destination rate and registering for schemes such as EU OSS. A tax engine is effectively required for international digital sales.

Where is tax stored on an order?

As a line-level breakdown on the order, captured at the time of purchase. Keep it — returns, reporting and audits all need the rate that applied then, not the rate that applies now.

[ Keep reading ]