# What Should a VAT Calculation Record Actually Contain?

A VAT calculation can look deceptively simple.

You send an amount and a country. The system returns a VAT amount.

For example:

```text
Net:   €100.00
VAT:    €19.00
Gross: €119.00
```

If all you need is the number right now, that may appear sufficient.

The problem starts six months later.

Someone asks:

- Why was 19% used?
- Which product tax class was selected?
- Which transaction date determined the rate?
- Was the input gross or net?
- Which store or checkout produced the calculation?
- Which rate dataset was used?
- Has the calculation logic changed since then?
- Can we reproduce the exact result?

If the only thing you stored was `19.00`, most of those questions are impossible to answer reliably.

A useful VAT calculation record therefore needs to preserve more than the result.

It needs to preserve enough **context, identity, and provenance** to explain how that result came into existence.

## The result is only one part of the record

A calculation record has several different responsibilities.

At minimum, it should let you answer four questions:

1. **What was calculated?**
2. **Which inputs and tax context were used?**
3. **Which rate and calculation logic produced the result?**
4. **Can the result be reproduced later?**

That means the record needs more than:

```json
{
  "vat": 1900
}
```

A more useful model starts looking like this:

```json
{
  "calculation_id": "e2d0940e-b123-47b7-80ad-8a3673fa4680",
  "country": "DE",
  "currency": "EUR",
  "vat_rate_bps": 1900,
  "gross_amount_minor": 11900,
  "net_amount_minor": 10000,
  "vat_amount_minor": 1900,
  "transaction_date": "2026-01-28T00:00:00Z"
}
```

Even this is only the beginning.

## 1. Start with a stable calculation identity

Every persisted calculation should have its own stable identifier.

For example:

```text
calculation_id
```

This gives other systems something durable to reference.

A checkout, support ticket, export, reconciliation process, or internal business record can retain that ID without copying every implementation detail.

It also makes later retrieval straightforward:

```text
GET /v1/transactions/{id}
```

In VAT Engine, `calculation_id` is the same identity exposed by the Transactions API rather than a second unrelated identifier.

That sounds like a small design choice, but duplicate identifiers quickly make financial systems harder to reason about.

One calculation should have one canonical identity.

## 2. Preserve the monetary values explicitly

A VAT calculation record should not store only the VAT amount.

You usually want all three values:

```text
net
VAT
gross
```

For example:

```json
{
  "net_amount_minor": 10000,
  "vat_amount_minor": 1900,
  "gross_amount_minor": 11900
}
```

This matters because one value should not need to be reverse-engineered later from another.

It also makes reconciliation easier.

If an external system says:

```text
Gross: €119.00
VAT:   €19.00
```

you can compare the recorded values directly rather than reconstructing them using whatever calculation rules happen to exist today.

VAT Engine represents monetary values using integer minor units.

So:

```text
€119.00 → 11900
€19.00  → 1900
```

This avoids making binary floating point the canonical representation of money.

I covered the reasoning in more detail here:

[Why Money Calculations Should Not Use Floating Point](https://vat-engine.app/blog/why-money-calculations-should-not-use-floating-point)

## 3. Record the rate — but not just the rate

You should obviously know which VAT rate was applied.

For example:

```json
{
  "vat_rate_bps": 1900
}
```

VAT Engine represents VAT rates in basis points:

```text
1900 = 19.00%
700  = 7.00%
```

But storing `1900` alone is not enough.

A rate without context raises another question:

> Why was this the applicable 19% rate?

That question requires provenance.

A calculation record should ideally distinguish between:

```text
the numeric rate
```

and:

```text
the evidence explaining where that rate came from
```

Those are not the same thing.

## 4. Transaction date is part of the tax context

Tax rates and treatment rules can change over time.

So the record should preserve the transaction or supply date used by the calculation.

For example:

```json
{
  "transaction_date": "2026-01-28T00:00:00Z"
}
```

This is particularly important for:

- refunds
- corrections
- historical imports
- migrations
- support investigations
- audits
- delayed reconciliation

Recalculating an old transaction with today's rate can produce a perfectly valid calculation for the wrong date.

That is why a VAT API should treat the date as part of the calculation context rather than incidental metadata.

## 5. Preserve the tax classification

Country alone does not determine VAT treatment.

The product or service classification matters too.

A record should therefore preserve whichever classification was actually used.

In a simple tax-class model that might be:

```json
{
  "tax_class_id": "standard"
}
```

Other systems may use:

- CN codes
- CPA codes
- HS codes
- merchant-defined tax categories
- governed mappings between classifications

The important rule is that the record should preserve the **actual classification input or resolved mapping**, not merely the resulting percentage.

Otherwise two calculations using the same numeric rate can become indistinguishable even though they reached that rate through different assumptions.

VAT Engine exposes its public tax-class catalogue here:

[Tax Classes API](https://vat-engine.app/docs/api/tax-classes)

## 6. Preserve whether the input included VAT

Consider these two requests:

```text
€100 net + 19% VAT
```

and:

```text
€100 gross including 19% VAT
```

They do not mean the same thing.

The first produces:

```text
Net:   €100.00
VAT:    €19.00
Gross: €119.00
```

The second requires VAT extraction:

```text
Net:   €84.03
VAT:   €15.97
Gross: €100.00
```

So the amount basis is part of the calculation contract.

VAT Engine makes this explicit through:

```json
{
  "price_includes_vat": true
}
```

or:

```json
{
  "price_includes_vat": false
}
```

A stored record should retain enough information to know which interpretation was used.

For more on the arithmetic:

[VAT-Inclusive vs VAT-Exclusive Pricing: The Math Developers Get Wrong](https://vat-engine.app/blog/vat-inclusive-vs-vat-exclusive-pricing)

## 7. Rate provenance should be a first-class field

A useful VAT record should not merely say:

```text
rate = 19%
```

It should be able to explain the status of the evidence behind that rate.

VAT Engine exposes that separately as:

```json
{
  "rate_provenance": {
    "schema_version": "rate-evidence/v1",
    "evidence_status": "legacy_unverifiable",
    "source": "legacy_recorded_window",
    "effective_at": "2026-01-28T00:00:00Z",
    "source_evidence": []
  }
}
```

The important part here is not the exact schema.

It is the distinction between:

```text
numeric result
```

and:

```text
strength of evidence behind that result
```

For example, historical data should not suddenly become `verified` merely because its numeric value happens to match a newer reviewed rate.

VAT Engine deliberately keeps older records marked as:

```text
legacy_unverifiable
```

when the required source-supported historical evidence was not captured.

That is more useful than pretending certainty exists where it does not.

## 8. Record which calculation logic produced the result

Rates are only half of the equation.

The arithmetic implementation itself can also change.

Imagine that a system changes:

- rounding behavior
- currency-scale handling
- inclusive VAT extraction
- validation rules
- allocation behavior

You now need to know which version created an old result.

That is why calculation provenance matters.

A record can retain fields such as:

```json
{
  "calculation_provenance": {
    "schema_version": "calculation-evidence/v1",
    "calculation_version": "vat-calculator-half-up/v1",
    "execution_digest": "...",
    "algorithm_manifest_version": "vat-algorithm-manifest/v1",
    "algorithm_manifest_digest": "...",
    "evidence_status": "captured",
    "replay_status": "ineligible"
  }
}
```

This gives the result a technical identity.

Instead of saying:

> "Our calculator currently returns this."

you can say:

> "This calculation was produced using this recorded calculation contract."

That distinction becomes increasingly important as financial software evolves.

## 9. Source attribution helps explain where the calculation came from

A merchant may have several systems producing VAT calculations:

```text
Shopify
WooCommerce
subscription billing
mobile checkout
POS
custom headless store
```

A stable source identifier helps distinguish them.

VAT Engine supports an optional:

```text
X-Source-ID
```

For example:

```text
headless-checkout
shopify-orders
stripe-subscriptions
```

That value can later appear as `source_id` in transaction history and reporting.

Importantly, the source tag does **not** change the VAT rate or amounts.

It is reporting and attribution metadata.

That separation is useful:

```text
tax inputs determine the calculation
source metadata tells you where the calculation originated
```

Source identifiers should also remain non-sensitive.

Do not put things like:

- customer names
- email addresses
- access tokens
- API credentials
- individual order numbers

into a general source tag.

## 10. Evidence status should not be reduced to a boolean

A tempting model is:

```json
{
  "verified": true
}
```

That usually loses too much information.

Evidence can exist in several meaningful states.

For example:

```text
verified
legacy_unverifiable
unavailable
```

Calculation evidence may similarly distinguish between captured evidence and records that cannot be proven to the same standard.

This makes uncertainty explicit.

Financial systems become difficult to audit when:

```text
unknown
```

is silently converted into:

```text
false
```

or:

```text
verified
```

A typed status is usually much safer.

## 11. Replay eligibility is different from evidence capture

Having evidence does not automatically mean you can reproduce a calculation exactly.

Exact replay requires enough preserved information to reconstruct the original decision without silently substituting today's data.

VAT Engine therefore treats replay as a separate state:

```text
not_supported
ineligible
available
```

Only records reporting:

```text
replay_status: available
```

are eligible for exact replay.

The replay endpoint can then use the original canonical request, pinned historical rate evidence, and registered calculator version:

```text
POST /v1/transactions/{id}/replay
```

A successful exact replay returns the reproduced result rather than asking the caller to choose a different rate version.

That distinction is important.

If the caller selects a newer historical dataset or a different calculator version, that is no longer replay.

It is a new, counterfactual calculation.

## 12. Idempotency belongs near the calculation boundary

A calculation record is also easier to trust when network retries cannot accidentally create different stored results.

For operations that can be retried, an idempotency key can bind the original request.

VAT Engine supports:

```text
Idempotency-Key
```

for authenticated calculations.

The same key with the same canonical request returns the existing calculation.

The same key with a different request fails instead of silently mutating the record.

That matters because distributed systems retry.

A timeout does not necessarily mean:

```text
the calculation failed
```

It may mean:

```text
the calculation succeeded but the response was lost
```

Stable calculation identity plus idempotency makes that situation much easier to handle safely.

## 13. A calculation record is not the same as a sales ledger

This distinction is especially important.

A VAT calculation can be useful evidence without being proof that a sale actually happened.

For example, a merchant could call a calculator while:

- previewing a checkout
- testing an integration
- quoting a price
- simulating a transaction
- debugging an order

That does not mean the transaction should automatically enter OSS reporting.

VAT Engine therefore separates:

```text
VAT calculation history
```

from:

```text
committed sales data
```

Calling:

```text
POST /v1/vat/calculate
```

records calculation history, but does **not** by itself add a sale to:

- OSS threshold monitoring
- Filing Prep
- OSS/IOSS reporting

Those workflows rely on committed supply data.

This prevents an API calculation from being mistaken for an accounting event.

The Transactions API documentation makes this distinction explicit:

[VAT Engine Transactions API](https://vat-engine.app/docs/api/transactions)

## 14. A record should preserve facts, not force conclusions

There is another architectural benefit to richer records.

You can improve interpretation later without rewriting history.

For example, if you preserve:

```text
transaction date
classification
amount basis
source
rate evidence
calculation version
result
```

then future tooling can review that record with more context.

If you preserve only:

```text
VAT = €19
```

most of that opportunity disappears.

This is why provenance-heavy systems can look verbose.

The extra fields are not there because JSON needs to be complicated.

They are there because financial decisions often need to survive longer than the code that originally produced them.

## What I would consider the minimum useful record

For a straightforward VAT calculation API, I would want at least:

```text
Stable calculation ID

Inputs:
- country / jurisdiction
- currency
- input amount
- whether VAT was included
- tax classification
- transaction date

Outputs:
- net amount
- VAT amount
- gross amount
- applied VAT rate

Context:
- source/store/channel identifier where useful

Evidence:
- rate evidence status
- rate source/version
- calculation version
- calculation evidence status
- replay status
```

For more complex tax systems, the model may need to go further.

For example, a transaction can contain separate tax or obligation components rather than one flat rate.

In that case the record should preserve those components individually rather than flattening them into a number that loses the calculation structure.

## What not to store as your only record

These patterns tend to cause trouble later.

### Only the final VAT amount

```json
{
  "vat": 1900
}
```

You cannot explain the rate, date, classification, or arithmetic.

### Only the rate

```json
{
  "rate": 0.19
}
```

You do not know which amount it applied to or why that rate was selected.

### A mutable reference to "current rate"

If historical records resolve through whatever the current configuration happens to be, old calculations can effectively change meaning.

### Money as floating point

```json
{
  "gross": 119.00
}
```

JSON may look harmless, but using binary floating point as the application's canonical monetary representation introduces unnecessary ambiguity.

### A generic `verified: true`

It hides whether rate evidence, calculation evidence, and replay evidence were actually available.

## The useful question is not "what did we calculate?"

The useful question is:

> Can we explain why this exact result exists?

That changes the shape of the system.

A calculator returns numbers.

A useful calculation record preserves:

```text
identity
inputs
context
result
rate evidence
calculation evidence
reproducibility
```

That makes debugging easier.

It makes reconciliation easier.

It makes historical review easier.

And it prevents future versions of your own software from silently rewriting the meaning of old calculations.

For financial APIs, that is usually worth a few extra fields.

---

You can see the current VAT Engine calculation contract here:

[Calculate VAT API](https://vat-engine.app/docs/api/calculate)

And the calculation-history/evidence model here:

[Transactions API](https://vat-engine.app/docs/api/transactions)

VAT Engine is currently in active alpha development. This article describes engineering and API design considerations and is not tax or legal advice.
