Reverse Charge in Software Billing: Decisions and Credit Notes
In many SaaS stacks, whether an invoice goes out without VAT depends on one field: reverse_charge=true on the customer record, set by someone in sales at some point. That holds until the customer sends a new VAT number, a downgrade triggers a credit note, or a tax auditor asks why the March invoice shows no VAT. Then you find out whether your billing system made and stored the decision, or whether it only exists on the PDF.
This article covers the logic behind it: which inputs the decision needs, how a VAT number's status is managed, what happens when that status changes mid-subscription, and how credit notes inherit the decision. The details the document itself must show, with a sample invoice and the EC Sales List reconciliation, are in the companion piece on creating a reverse charge invoice.
Decide the tax before the invoice exists, not at export
A common setup: checkout collects a country, the billing system issues an invoice, and accounting fixes whatever is wrong when the data goes to DATEV or the ERP. That's the wrong way round. Once an invoice has left the system, you can only correct it with another document, such as a cancellation or a credit note, and each of those has to match the EC Sales List and the VAT return again.
So the decision belongs where the invoice line is created: after rating, before rendering. With usage-based billing it isn't made once per customer. It's made again on every invoice, because the customer's status can change between two billing runs. The legal basis, the customer accounting for VAT on cross-border B2B services, is in the EU VAT Directive 2006/112/EC. Your system only has to map it onto data correctly.
Five inputs, one decision table
For digital services supplied by a German seller, the decision needs five inputs: whether the customer is a business, its country of establishment, the status of its VAT number, the type of supply, and the entity issuing the invoice. They lead to only a handful of outcomes:
| Customer | VAT number status | Outcome |
|---|---|---|
| Business in Germany | irrelevant | German VAT |
| Business in another EU country | valid, checked | Reverse charge |
| Business in another EU country | invalid or not checked | Review, no automatic release |
| Consumer in another EU country | none | Customer-country VAT via OSS; below the €10,000 threshold German VAT may apply |
| Customer outside the EU | not applicable | Separate rule, no Section 14a note and no EC Sales List |
The third row matters most. A business customer without a confirmed number is neither automatically reverse charge nor automatically B2C. Pick any default here and you'll issue wrong invoices the first time VIES rejects a number. The €10,000 threshold (Section 3a(5) UStG) only applies to supplies to non-business customers and belongs in your OSS automation.
A VAT number's status is a state with a timestamp
A VAT number isn't a master data field you fill in once. It has a state, and that state has a time. A workable model has at least these states:
unchecked: captured, not yet checked. Blocks any invoice without VAT.valid: check succeeded, with stored result and timestamp.invalid: check rejected. The next invoice goes to review.unavailable: the member state's service didn't respond. Retry; don't guess.
unavailable is the state teams forget. VIES queries the member states' databases in real time, and individual countries are sometimes unreachable. A system that treats this as invalid suddenly charges German VAT. One that treats it as valid releases an unchecked number. Both are wrong: hold the invoice and retry the check.
German sellers can also run a qualified confirmation request with the Federal Central Tax Office (BZSt), which matches name and address as well. When to re-check and how to automate the request is covered in our article on automating VIES VAT ID checks.
Store the decision as a record
If the decision only exists as an outcome on the PDF, you can't justify it later. Store an immutable record of inputs and result for each invoice line:
tax_decision
invoice_line_id inv_2026_001842_l1
decided_at 2026-11-03T06:00:12Z
rule_version eu-b2b-2026-01
customer_country FR
customer_type business
vat_id FR99999999999
vat_check_id chk_7f3a (result valid, 2026-10-28)
supply_type saas_subscription
treatment reverse_charge
rate 0
category_code AE
note_key reverse_charge_recipient_liable
Three fields do the work. vat_check_id points to the stored check result, not to whatever is on the customer record today. rule_version shows which rule applied, even after you change the rule. And note_key pulls the invoice wording from the rule, so the PDF template doesn't decide it on its own.
When an input changes, new lines get a new record. Existing records are never overwritten.
When the status changes mid-subscription
A walkthrough with made-up numbers. A customer in Vienna signs up on 1 February for a plan at €600 net per month, billed monthly in advance. They don't enter a VAT number at signup, so your system treats them as a consumer and charges Austrian VAT through OSS. On 14 February they add a VAT number, and the check returns valid.
- The February invoice from 1 February stays as it is. Whether to cancel it and reissue it under reverse charge is a question for your tax adviser. The system has to support both paths without changing the original.
- The March invoice on 1 March gets a new decision record: reverse charge, referencing the check from 14 February.
- An upgrade on 20 March creates a prorated line. It uses the status at the time it's created, not the status at the start of the month.
The reverse case is more dangerous. At renewal on 1 May, VIES reports the number as invalid, for instance after a restructuring on the customer's side. A system with a customer flag keeps invoicing without VAT. A system with a state model holds the May invoice until someone checks the new number or sets the tax treatment another way. If the new number belongs to a different legal entity, create a new customer record. The old invoices stay linked to the old number.
Credit notes inherit the original invoice's decision
In May the same customer gets a €150 credit for an outage in March. This means a commercial credit note correcting an invoice, not self-billing. A typical mistake: the credit note is treated like a new invoice and runs through today's tax decision. If the status in May differs from March, the credit note shows a different tax treatment from the invoice it corrects, and the totals in the EC Sales List and VAT return stop matching.
The rule is simple. A credit note refers to a specific original line and copies its decision record, including VAT number, tax category and invoice wording. The same goes for prorated credits on downgrades, for cancellations and for refunds. Your system shouldn't allow a credit note without a reference to an original line at all.
Six test cases before go-live
Before reverse charge runs in production, these cases should pass as automated tests in the billing system. Each checks a specific outcome:
- EU business with a valid number: reverse charge, category
AE, invoice wording present, no tax amount. - Same number with check result
invalid: invoice goes to review, nothing is sent. - Check service unreachable: status
unavailable, invoice waits, retry is scheduled. - VAT number added mid-month: only new lines change, the original stays untouched.
- Credit note after a status change: takes the original line's decision, not the current status.
- Rule change: old invoices still show the old
rule_version.
Kontier stores the tax decision per invoice line with the check evidence and rule version, and derives credit notes from the original line. It's part of the European billing workflow.
Not tax or legal advice. This post describes the law and common practice in general terms. For your own case, ask your tax adviser. As of: