Guides··9 min read

WooCommerce Tax-Exempt Customers: The Complete Setup Guide

If you sell to wholesalers, resellers, government agencies, or non-profits, some of your customers shouldn't pay sales tax at checkout. Get this wrong and you'll either overcharge exempt buyers (they'll complain, or worse, churn) or miss collecting tax you were supposed to remit (and eat the bill yourself during an audit).

This guide covers everything you need: when exemptions apply, how WooCommerce handles them out of the box, three ways to configure them (native, code, plugin), and the audit trail you should keep regardless of which path you pick.


When does a customer qualify as tax-exempt?

Tax-exempt status is never automatic. It's always based on a document the customer provides — you don't guess, you collect evidence. The common cases:

  • US resellers — hold a resale certificate (Form ST-5, ST-120, or state-specific). They don't pay sales tax because they're going to charge it when they resell.
  • Non-profits — hold a 501(c)(3) determination letter and often a state-level exemption certificate.
  • Government agencies — federal, state, or municipal buyers presenting a purchase order and tax-exemption ID.
  • EU B2B (reverse charge) — VAT-registered businesses buying cross-border within the EU. They self-account for VAT; you don't charge it but you still report the transaction.
  • Diplomatic and international organizations — rare, but they'll ask.

The common thread: the customer is responsible for giving you the document, and you're responsible for storing it. If an auditor later asks why you didn't collect tax from John at Acme Wholesale, you need to produce the certificate and the reason. This matters later when we talk about audit trails.


How WooCommerce handles tax exemption natively

WooCommerce has a built-in flag for this: a customer-level boolean called is_vat_exempt. When it's set to true for a session, WooCommerce removes all tax lines during cart/checkout calculation. The flag is respected by:

  • The core WooCommerce tax engine
  • Cart & Checkout blocks (via the Store API)
  • Most third-party tax services (TaxJar, Avalara, etc.) when you route calculation through their filters

The catch: WooCommerce doesn't give you a UI to set this flag. There's no checkbox on the user edit screen. There's no admin list of exempt customers. The flag only exists if you set it programmatically — which brings us to the three approaches below.


Approach 1: Set is_vat_exempt with code

If you have one or two exempt customers and never plan to add more, a small snippet in your theme's functions.php or a custom plugin will do it:

add_filter( 'woocommerce_customer_get_is_vat_exempt', function ( $is_exempt, $customer ) {
    $exempt_user_ids = [ 42, 57, 103 ]; // User IDs to exempt
    if ( in_array( $customer->get_id(), $exempt_user_ids, true ) ) {
        return true;
    }
    return $is_exempt;
}, 10, 2 );

This works, but it has four problems the moment you grow past a handful of exemptions:

  1. No searchable record of who's exempt. You're grepping PHP files.
  2. No reason field. If an auditor asks why user 42 is exempt, you'd better remember.
  3. Deploying is a code change. Your support team can't add a new reseller without a developer.
  4. No role-based logic. If you want every user with the wholesale_customer role to be exempt automatically, you have to fetch roles in the filter and keep that logic in sync with your role management.

Good for solo stores. Doesn't scale.


Approach 2: Hide taxes for whole user roles

A middle-ground trick: hook into woocommerce_customer_get_is_vat_exempt and check the user's roles instead of a hardcoded ID list.

add_filter( 'woocommerce_customer_get_is_vat_exempt', function ( $is_exempt, $customer ) {
    $exempt_roles = [ 'wholesale_customer', 'reseller', 'non_profit' ];
    $user = get_user_by( 'id', $customer->get_id() );
    if ( $user && array_intersect( $exempt_roles, $user->roles ) ) {
        return true;
    }
    return $is_exempt;
}, 10, 2 );

This is better than the hardcoded list because onboarding a new exempt customer becomes: assign them a role. Your e-commerce manager can do that from the user edit screen.

But you still have the audit problem. And you can't make exceptions — if one customer in your wholesale_customer role is actually supposed to pay tax (maybe they're in a state where resale doesn't apply to your product), you're stuck.


Approach 3: Use a tax exemption plugin

For any store with more than a few exempt customers, or any store that needs to pass an audit, you want:

  • A searchable admin list of all exempt customers and exempt roles
  • A reason field on each exemption (e.g., "Resale certificate #ST-120-Q4-2025")
  • An audit trail: who granted the exemption, when, and when it was revoked
  • Role-based exemptions plus per-user overrides
  • Compatibility with HPOS, Cart & Checkout blocks, and persistent object caches

This is what our WooCommerce Tax Exempt plugin gives you. You mark any user or role as exempt from a single admin screen under WooCommerce → Tax Exemptions. Every change is logged with the admin who made it, a timestamp, and the reason you entered.

Under the hood it sets the same is_vat_exempt flag WooCommerce already respects — so it works with TaxJar, Avalara, the blocks checkout, and anything else that honors the native flag. No weird forks, no JavaScript hacks at checkout.


Setting up tax-exempt customers (step by step)

Here's the workflow we recommend, regardless of which approach above you use.

1. Make sure WooCommerce taxes are enabled

Go to WooCommerce → Settings → General and check "Enable taxes and tax calculations". This sounds obvious, but we see stores every week that disabled taxes globally thinking it would make exempt customers work — it just turns off tax for everyone.

2. Collect the exemption document before you flip the switch

When a customer says "I shouldn't be charged tax", your response is not "Sure, let me fix that." It's "Can you send me your resale certificate / 501(c)(3) letter / VAT number?"

Store that document. Email it to yourself, upload it to the CRM, whatever — but keep it. An auditor will ask.

3. Decide: user-level or role-level?

  • User-level if this customer is an exception. You have 500 wholesale customers and one of them is exempt for reasons specific to them.
  • Role-level if exemption follows a category. Every wholesale_customer gets it, because you only create that role for verified resellers.

In practice, most B2B stores end up with both: a role-level rule for the common case, plus a handful of user-level overrides for edge cases.

4. Enter the reason at the moment of exemption

Even if you're just setting is_vat_exempt in code — add a comment. Better: log it somewhere queryable. This is the single most common failure mode during audits: exemptions exist, but there's no record of why.

A good reason format:

  • "Resale certificate ST-120, NY, expires 2027-12-31"
  • "501(c)(3), Section 501 letter on file in Google Drive / compliance-docs"
  • "EU B2B reverse charge, VAT IT12345678901, validated via VIES on 2026-03-15"

5. Test the checkout

Create a test user, apply the exemption, add a taxed product to the cart. You should see:

  • No tax line in the cart totals
  • No tax line in the order email
  • No tax line in the PDF invoice (if you're using one)

If you see tax disappear in the cart but reappear in the order email, you probably have a caching or email-template issue — not an exemption logic issue.

6. Verify with external tax services (if applicable)

If you use TaxJar, Avalara, or another service: their plugin should honor is_vat_exempt and skip the API call entirely for exempt customers. If it doesn't, check their filter priority — sometimes their tax hooks run before the exemption flag is set and they still hit the API. All major services have a setting for this.


EU VAT and reverse charge — the special case

If you're an EU seller with EU B2B customers, exemption isn't exactly exemption — it's reverse charge. You don't charge VAT, but you're still required to:

  • Collect and validate the customer's VAT number (via the VIES service)
  • Include a reverse charge note on the invoice (e.g., "VAT reverse charge — Art. 196 Directive 2006/112/EC")
  • Report the intra-EU B2B sale on your VAT return

None of that is handled by is_vat_exempt alone. You'll want either a dedicated EU VAT plugin (which validates VAT numbers via VIES and adds the reverse charge note) or code that:

  1. Validates the VAT number at checkout
  2. Sets is_vat_exempt = true if validation passes and the customer is in a different EU country
  3. Modifies the invoice template to include the reverse charge clause

If your store does meaningful EU B2B volume, don't roll this yourself. VIES validation is flaky, invoice compliance varies by country, and getting it wrong is expensive.


Building the audit trail

The single most important thing after "the customer isn't charged tax" is "I can prove why." Every exemption should produce a record with:

FieldExample
Who[email protected]
When2026-03-12 14:08:22 UTC
Who was exempteduser 1042 ([email protected])
ReasonResale certificate ST-120, filed in /compliance/2026-Q1/
Ended (if revoked)2026-12-31 23:59:59 UTC

If you're rolling your own with code, at minimum write this to a log table or a dedicated post type. If you're using Tax Exempt, the audit trail is built in — every grant and revoke is recorded automatically with the admin user, timestamp, and reason.


Common mistakes

Disabling taxes globally instead of per-customer. If you turn off taxes in settings to "fix" one exempt customer, you stop collecting from everyone. Classic rookie move.

Applying exemption at the product level. Some stores make tax-exempt product categories. That works if the product is always exempt (e.g., unprepared food in some states) but not if it's the customer who's exempt. Mixing the two logic types will bite you.

Forgetting to revoke expired certificates. A reseller's certificate may expire. If you don't track expiration dates and revoke automatically, you'll have exempt customers who shouldn't be. Calendar reminders help; better yet, plugins with expiration dates built in.

Trusting the customer's word. "I'm a non-profit" is not a document. Don't flip the switch until you have the paperwork.


The short version

  1. WooCommerce has native tax-exemption support via is_vat_exempt — you just need a UI to control it
  2. For one or two exempt users, a PHP snippet is fine
  3. For a growing B2B store, use a plugin with role-based rules, a reason field, and an audit log
  4. Always collect the document before applying the exemption
  5. EU B2B is a special case — don't treat reverse charge like a simple exemption