Skip to main content

Calculate taxes on your backend

Calculating taxes shouldn't be hard and tedious. With Quaderno it's as easy as this:

curl https://ACCOUNT_NAME.sandbox-quadernoapp.com/api/tax_rates/calculate?to_country=US&to_postal_code=10128 \
-u YOUR_API_KEY:x

The parameters to_country and to_postal_code represent your customer's location. All other data is inferred from your account's default configuration (that's why configuring your account is so important!).

You don't even need to specify any amount, as you may prefer to do the calculation on your own once you get the correct tax_rate.

Now, depending on your account's configuration you may get very different responses! In this case we got a response that contains "name": "Sales tax" and "rate": 8.875, which corresponds to "city": "NEW YORK".

Here's the full response based on our Sandbox configuration:

{
"city": "NEW YORK",
"country": "US",
"county": "NEW YORK",
"currency": "EUR",
"name": "Sales tax",
"notes": null,
"notice": "Only state tax rates are returned in test mode. Local tax rates will be returned only in live mode except for the following test zip codes: 90049,10128,60611,33132.",
"product_type": "good",
"rate": 8.875,
"region": "NY",
"tax_behavior": "exclusive",
"tax_code": "standard",
"taxable_part": 100.0,
"import": true,
"subtotal": null,
"tax_amount": null,
"total_amount": null,
"status": "taxable"
}

The status response field

Check the status response param to understand what happens on tax calculations:

  • taxable: the transaction is taxable and the tax calculator will return a non-zero rate.
  • non_taxable: The transaction is non taxable. This could happen because the particular product is not taxable (ie. a digital product in California), or because the region for a particular territory within your registered jurisdiction is exempted (for example, you're registered in France selling to a customer from New Caledonia).
  • not_registered: You're trying to calculate taxes in a jurisdiction you're not registered in!
  • reverse_charge: The transaction is a "reverse charge" transaction, which happens on B2B sales. It means you're not responsible of collecting taxes for it (the buyer will), so they won't be calculated. You can read more about the reverse charge mechanism in our blog.
info

On Quaderno Sandbox, only state tax rates are returned for all US addresses, except for the following ZIP codes: 90049, 10128, 60611, 33132.

Let's see an example response trying to calculate taxes in a jurisdiction where you're not registered for tax collection. Quaderno will return an not_registered status, along with null name and a 0.0 rate:

{
"country": "CA",
"currency": "EUR",
"name": null,
"notes": null,
"product_type": "service",
"rate": 0.0,
"region": null,
"tax_behavior": "exclusive",
"tax_code": "eservice",
"taxable_part": null,
"import": true,
"subtotal": null,
"tax_amount": null,
"total_amount": null,
"status":"not_registered"
}

Depending on your use case, you may need to craft a more explicit request, overriding some product and account-level settings.

Advanced use cases

Selling multiple product types

When selling different products, you probably want to avoid relying on the default settings and specify tax_code and product_typeon each request.

In Quaderno you can use the following tax codes: eservice, ebook, saas, consulting, standard, reduced and exempt.

These, along with product_type which can be good or service, define what you're selling.

Building up on our previous example:

curl https://YOUR_ACCOUNT_NAME.sandbox-quadernoapp.com/api/tax_rates/calculate?to_country=US&to_postal_code=10128&product_type=service&tax_code=ebook \
-u YOUR_API_KEY:x

For our Sandbox Belgian based account, registered in the EU's VAT OSS / IOSS jurisdiction, we'll get a response like:

{
"country": "FR",
"currency": "EUR",
"name": "TVA",
"notes": null,
"product_type": "service",
"rate": 5.5,
"region": null,
"tax_behavior": "exclusive",
"tax_code": "ebook",
"taxable_part": 100.0,
"import": true,
"subtotal": null,
"tax_amount": null,
"total_amount": null,
"status": "taxable"
}

Calculating taxable amounts

You can get your tax_rate and calculate whatever you need based on that, or we can do the math for you by specifying a concrete amount, like 9.95:

curl https://YOUR_ACCOUNT_NAME.sandbox-quadernoapp.com/api/tax_rates/calculate?to_country=US&to_postal_code=10128&product_type=service&tax_code=ebook&amount=9.95 \
-u YOUR_API_KEY:x

Which returns:

{
"country": "FR",
"currency": "EUR",
"name": "TVA",
"notes": null,
"product_type": "service",
"rate": 5.5,
"region": null,
"tax_behavior": "exclusive",
"tax_code": "ebook",
"taxable_part": 100.0,
"import": true,
"subtotal": 9.95,
"tax_amount": 0.55,
"total_amount": 10.5,
"status": "taxable"
}

Taxable base is the amount you must use to calculate the tax amount.

The taxable part is the percentage of the subtotal to be used for calculating the tax amount. It's usually 100% but there are a few exceptions. For example, in Texas the taxable base is 80% for SaaS products.

Selling to businesses (B2B sales)

When including the tax_id parameter on your API calls to /tax_rates/calculate, Quaderno will automatically check its validity and apply the reverse_charge status when needed.

Quaderno can validate tax IDs from the EU, United Kingdom, Switzerland, Québec (Canada), Australia, and New Zealand. You can also validate tax IDs on a separate API call to /tax_ids/validate.

For instance, this API call:

curl https://YOUR_ACCOUNT_NAME.sandbox-quadernoapp.com/api/tax_rates/calculate?to_country=DE&tax_id=DE123456789&amount=100 \
-u YOUR_API_KEY:x

Will return:

{
"country": "DE",
"currency": "EUR",
"name": null,
"notes": "Tax amount subject to reverse charge",
"product_type": "service",
"rate": 0.0,
"region": null,
"tax_behavior": "exclusive",
"tax_code": "eservice",
"taxable_part": null,
"import": false,
"subtotal": 100.0,
"tax_amount": 0.0,
"total_amount": 100.0,
"status": "reverse_charge"
}
tip

When your customers provide a tax ID that cannot be validated automatically, the safest route is to charge tax. Eventually, the customer could apply for a refund of that tax at his local tax office. Not charging a customer who should pay the tax, could cause trouble with the tax agencies. When in doubt, always consult your tax advisor.

Selling from different places

You can override the seller's account address by specifying your own from_country.

This makes sense when shipping physical products from a warehouse placed in a different jurisdiction than the one where your bussiness is based.

Shipping taxes

When selling physical goods, you may need to charge shipping costs and with it, the corresponding taxes.

We always recommend to add the shipping cost as a separate line item in the invoice. As per which tax to apply, when there are several products with different tax codes, the recommendation is to apply the highest tax rate. When in doubt, always just apply the Standard tax code to the shipping line item.

Jurisdictions with additional taxes

For jurisdictions that need to apply two tax rates (e.g. some Canadian provinces), the response will include the second tax rate on fields with the additional_ suffix, like so:

{
"additional_name": "QST",
"additional_rate": 9.975,
"additional_taxable_part": 100.0,
"country": "CA",
"currency": "CAD",
"name": "GST",
"notes": null,
"product_type": "service",
"rate": 5.0,
"region": "QC",
"tax_behavior": "exclusive",
"tax_code": "standard",
"taxable_part": 100.0,
"import": false,
"subtotal": 100.0,
"tax_amount": 5.0,
"additional_tax_amount": 9.98,
"total_amount": 114.98,
"status":"taxable"
}

Calculating taxes for Connect accounts

Quaderno Connect is a powerful API and set of tools that can be used to add tax management and invoicing features to business models like marketplaces and software platforms. You can learn more about Quaderno Connect on its guides.

All the examples shown in this guide will apply your connected account settings to the tax calculation, by sending an Authorization: Bearer {{SELLER_ACCESS_TOKEN}} header to act on your user's behalf.