Build the Dutch VAT Return (BTW-aangifte)
Skill: Convert a VAT ledger into the Dutch BTW-aangifte (SBR/XBRL) dataset
Region: Netherlands (Nederland)
Category: VAT — BTW-aangifte (Omzetbelasting) via Digipoort / SBR
Does: Takes a period VAT ledger (sales and purchases by rate and scheme) and produces the BTW-aangifte rubriek dataset as an SBR XBRL instance (xbrli:xbrl) — mapping turnover and tax to rubrieken 1a–5g of the Belastingdienst Aangifte omzetbelasting, ready for transmission to Digipoort.
Standard: SBR Nederlandse Taxonomie (NT) — bd-rpt-ob-aangifte entrypoint; submitted to the Belastingdienst via Digipoort (Logius) as a Digikoppeling/SBR message.
The actual filing is an XBRL instance wrapped in an SBR aanleverbericht and sent over Digipoort with PKIoverheid authentication — not a free-standing file you upload in a portal. The Belastingdienst recomputes rubriek 5c/5g from the sub-rubrieken, so every amount must reconcile exactly. The Nederlandse Taxonomie version changes yearly (NT19 for tax year 2026); validate the instance against the active NT entrypoint and the Belastingdienst rubriek validations before sending. Amounts are whole euros (no cents).
When this applies
- A Dutch VAT entrepreneur (ondernemer voor de btw) files the periodic aangifte omzetbelasting — monthly, quarterly, or yearly per the Belastingdienst-assigned tijdvak.
- The return is due and paid by the last day of the month following the tijdvak (e.g. Q1 2026 → 30 April 2026). Late filing/payment triggers a verzuimboete.
- If the entrepreneur reports intra-EU supplies, the BTW-aangifte rubriek 3b must reconcile with the separate ICP-opgaaf (Opgaaf intracommunautaire prestaties).
- Excluded: entrepreneurs fully under the KOR (kleineondernemersregeling) who are deregistered from VAT do not file periodic returns; a one-off suppletie corrects a previously filed period.
Conversion procedure
- Read the source. Accept a VAT ledger as CSV/JSON (columns: date, counterparty, country, net amount, VAT rate, VAT amount, scheme tag) or a trial-balance extract. Parse rows directly; OCR/
pdftotexta scanned grootboek first. - Classify each line. Tag every transaction to exactly one rubriek by (a) direction (output vs. input), (b) rate (21% hoog, 9% laag, 0%/vrijgesteld), and (c) scheme (binnenland, verlegging, intra-EU levering, export, EU-verwerving, invoer).
- Aggregate per rubriek. Sum net omzet into the left column and omzetbelasting into the right column of each rubriek 1a–4b. Round each line to whole euros.
- Compute the totals. Derive 5a (verschuldigde omzetbelasting) = Σ output tax; 5b (voorbelasting) = deductible input tax; 5c = 5a − 5b; add 5d/5e/5f if applicable; 5g = amount payable/refundable.
- Apply KOR / corrections. If the entrepreneur uses the (degressieve) KOR, handle per the assigned regime; flag any suppletie of a prior period (filed separately if ≥ €1,000 net correction).
- Emit the XBRL instance. Build
xbrli:xbrlwith the NT schemaRef, a single period context (instant/duration as the taxonomy requires), the EUR unit, and one fact per non-zero rubriek using thebd-bedr:concept names. - Validate. Run the checklist; confirm 3b ties to the ICP-opgaaf and 5c/5g reconcile.
Source → BTW-aangifte rubriek map
| From the source | → Rubriek (omzet / btw) | XBRL concept (btw amount) |
|---|---|---|
| Domestic sales taxed at 21% | 1a | bd-bedr:OmzetbelastingHoogTariefVerschuldigd |
| Domestic sales taxed at 9% | 1b | bd-bedr:OmzetbelastingLaagTariefVerschuldigd |
| Sales at other/special rates | 1c | bd-bedr:OmzetbelastingOverigeTarievenVerschuldigd |
| Private use (privégebruik) | 1d | bd-bedr:OmzetbelastingPriveGebruikVerschuldigd |
| Domestic 0% / vrijgesteld supplies | 1e | (omzet only) bd-bedr:LeveringenDienstenBelastNulOfVrijgesteldOmzet |
| Supplies where VAT is reverse-charged to you | 2a | bd-bedr:OmzetbelastingNaarUVerlegdVerschuldigd |
| Intra-EU supplies of goods/services (to ICP) | 3b | bd-bedr:LeveringenNaarLandenBinnenEUOmzet |
| Exports outside the EU | 3a | bd-bedr:LeveringenNaarLandenBuitenEUOmzet |
| Installation/distance sales in other EU states | 3c | bd-bedr:InstallatieAfstandsverkopenBinnenEUOmzet |
| Intra-EU acquisitions of goods | 4b | bd-bedr:VerwervingenVanGoederenUitLandenBinnenEUVerschuldigd |
| Supplies/services from outside the EU (reverse charge) | 4a | bd-bedr:LeveringenDienstenUitLandenBuitenEUVerschuldigd |
| Total output VAT (1a+1b+1c+1d+2a+4a+4b) | 5a | bd-bedr:OmzetbelastingTotaalVerschuldigd |
| Deductible input VAT (voorbelasting) | 5b | bd-bedr:VoorbelastingTotaal |
| Subtotal (5a − 5b) | 5c | bd-bedr:OmzetbelastingVoorbelastingSaldo |
| Reduction under KOR (if applicable) | 5d | bd-bedr:VerminderingKleineondernemersregeling |
| Estimate/prior corrections | 5e/5f | bd-bedr:OmzetbelastingSchattingVorigeAangifte* |
| Total payable / refundable | 5g | bd-bedr:OmzetbelastingTeBetalenTerugTeVragen |
Each rubriek 1a–1c carries two facts: the net omzet (left column) and the btw (right column). Reverse-charge and acquisition rubrieken (2a, 4a, 4b) carry both the taxable base and the tax you owe on it.
Document structure
xbrli:xbrl (root; declares NT namespaces)
├── link:schemaRef (→ bd-rpt-ob-aangifte entrypoint .xsd)
├── xbrli:context id="ctx" (one reporting context)
│ ├── xbrli:entity / xbrli:identifier (scheme = .../omzetbelastingnummer; OB-nr)
│ └── xbrli:period / xbrli:startDate + endDate (the tijdvak)
├── xbrli:unit id="EUR" → iso4217:EUR
└── facts (one element per reported rubriek)
├── bd-bedr:LeveringenDienstenBelastMetHoogTariefOmzet (1a omzet)
├── bd-bedr:OmzetbelastingHoogTariefVerschuldigd (1a btw)
├── bd-bedr:LeveringenDienstenBelastMetLaagTariefOmzet (1b omzet)
├── bd-bedr:OmzetbelastingLaagTariefVerschuldigd (1b btw)
├── bd-bedr:LeveringenNaarLandenBinnenEUOmzet (3b)
├── bd-bedr:OmzetbelastingTotaalVerschuldigd (5a)
├── bd-bedr:VoorbelastingTotaal (5b)
└── bd-bedr:OmzetbelastingTeBetalenTerugTeVragen (5g)
All monetary facts carry contextRef="ctx", unitRef="EUR", and decimals="0" (whole euros). Only report rubrieken with a value; omitted rubrieken are treated as nil by the Belastingdienst. The instance is embedded in the SBR aanleverbericht for Digipoort, which adds the routing/authentication envelope.
Code tables
Rate → rubriek (output side)
| Rate | Rubriek | Note |
|---|---|---|
| 21% (hoog tarief) | 1a | Standard rate |
| 9% (laag tarief) | 1b | Reduced rate (food, books, medicines, etc.) |
| Other/special | 1c | e.g. margin scheme components |
| Privégebruik | 1d | Deemed supply for private use |
| 0% / vrijgesteld | 1e | Zero-rated or exempt domestic |
Reverse-charge & cross-border rubrieken
| Rubriek | Meaning |
|---|---|
| 2a | Verleggingsregelingen binnenland — VAT reverse-charged to you by a domestic supplier (e.g. construction, scrap) |
| 3a | Leveringen naar landen buiten de EU (export, 0%) |
| 3b | Leveringen/diensten naar landen binnen de EU (intra-EU; must match ICP-opgaaf) |
| 3c | Installatie/afstandsverkopen binnen de EU |
| 4a | Leveringen/diensten uit landen buiten de EU (reverse charge to you) |
| 4b | Verwervingen van goederen uit landen binnen de EU (intra-EU acquisitions) |
Tijdvak (period) examples
| Tijdvak | startDate | endDate | Due date |
|---|---|---|---|
| Q1 2026 | 2026-01-01 | 2026-03-31 | 2026-04-30 |
| Maart 2026 (monthly) | 2026-03-01 | 2026-03-31 | 2026-04-30 |
| Jaar 2026 (yearly) | 2026-01-01 | 2026-12-31 | 2027-01-31 |
Calculation rules
- Output VAT per rate = net omzet × rate, rounded to whole euros. (e.g. 21% × €100,000 = €21,000.)
- Rubriek 5a (verschuldigde omzetbelasting) = 1a btw + 1b btw + 1c btw + 1d btw + 2a btw + 4a btw + 4b btw.
- Rubriek 5b (voorbelasting) = total deductible input VAT on purchases, imports, intra-EU acquisitions (4b VAT is both owed in 5a and deductible in 5b when fully deductible), and reverse-charge VAT (2a/4a) where deductible.
- Rubriek 5c = 5a − 5b.
- Rubriek 5g (te betalen / terug te vragen) = 5c − 5d (KOR reduction) ± 5e/5f corrections. A positive 5g is payable; negative is refundable.
- 3b must equal the total of the same period's ICP-opgaaf (intra-EU supplies); a mismatch is a common rejection cause — flag it.
- All amounts in whole euros (
decimals="0"); the Belastingdienst rounds in the taxpayer's favour per line. Never copy a printed total — recompute every rubriek from the ledger and flag discrepancies.
Worked example (end-to-end)
A quarterly filer, Voorbeeld Handel BV, OB-nummer NL001234567B01, tijdvak Q1 2026 (2026-01-01 → 2026-03-31):
| Item | Net omzet | BTW |
|---|---|---|
| Domestic sales 21% (1a) | €100,000 | €21,000 |
| Domestic sales 9% (1b) | €20,000 | €1,800 |
| Intra-EU supplies (3b) | €30,000 | €0 |
| Voorbelasting on purchases (5b) | — | €8,500 |
Totals: 5a = 21,000 + 1,800 = 22,800; 5b = 8,500; 5c = 5g = 14,300 payable.
<?xml version="1.0" encoding="UTF-8"?>
<xbrli:xbrl
xmlns:xbrli="http://www.xbrl.org/2003/instance"
xmlns:link="http://www.xbrl.org/2003/linkbase"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:iso4217="http://www.xbrl.org/2003/iso4217"
xmlns:bd-bedr="http://www.nltaxonomie.nl/nt19/bd/20251203/dictionary/bd-bedragen">
<link:schemaRef xlink:type="simple"
xlink:href="http://www.nltaxonomie.nl/nt19/bd/20251203/entrypoints/bd-rpt-ob-aangifte.xsd"/>
<xbrli:context id="ctx">
<xbrli:entity>
<xbrli:identifier scheme="http://www.belastingdienst.nl/omzetbelastingnummer">NL001234567B01</xbrli:identifier>
</xbrli:entity>
<xbrli:period>
<xbrli:startDate>2026-01-01</xbrli:startDate>
<xbrli:endDate>2026-03-31</xbrli:endDate>
</xbrli:period>
</xbrli:context>
<xbrli:unit id="EUR">
<xbrli:measure>iso4217:EUR</xbrli:measure>
</xbrli:unit>
<bd-bedr:LeveringenDienstenBelastMetHoogTariefOmzet contextRef="ctx" unitRef="EUR" decimals="0">100000</bd-bedr:LeveringenDienstenBelastMetHoogTariefOmzet>
<bd-bedr:OmzetbelastingHoogTariefVerschuldigd contextRef="ctx" unitRef="EUR" decimals="0">21000</bd-bedr:OmzetbelastingHoogTariefVerschuldigd>
<bd-bedr:LeveringenDienstenBelastMetLaagTariefOmzet contextRef="ctx" unitRef="EUR" decimals="0">20000</bd-bedr:LeveringenDienstenBelastMetLaagTariefOmzet>
<bd-bedr:OmzetbelastingLaagTariefVerschuldigd contextRef="ctx" unitRef="EUR" decimals="0">1800</bd-bedr:OmzetbelastingLaagTariefVerschuldigd>
<bd-bedr:LeveringenNaarLandenBinnenEUOmzet contextRef="ctx" unitRef="EUR" decimals="0">30000</bd-bedr:LeveringenNaarLandenBinnenEUOmzet>
<bd-bedr:OmzetbelastingTotaalVerschuldigd contextRef="ctx" unitRef="EUR" decimals="0">22800</bd-bedr:OmzetbelastingTotaalVerschuldigd>
<bd-bedr:VoorbelastingTotaal contextRef="ctx" unitRef="EUR" decimals="0">8500</bd-bedr:VoorbelastingTotaal>
<bd-bedr:OmzetbelastingVoorbelastingSaldo contextRef="ctx" unitRef="EUR" decimals="0">14300</bd-bedr:OmzetbelastingVoorbelastingSaldo>
<bd-bedr:OmzetbelastingTeBetalenTerugTeVragen contextRef="ctx" unitRef="EUR" decimals="0">14300</bd-bedr:OmzetbelastingTeBetalenTerugTeVragen>
</xbrli:xbrl>
Normalisations shown: rate 21% → btw recomputed 21% × 100,000 = 21,000; 9% → 1,800; amounts to whole euros (decimals="0", no cents); 5a recomputed 21,000 + 1,800 = 22,800; 5g recomputed 22,800 − 8,500 = 14,300. The OB-nummer is carried in the entity identifier under the omzetbelastingnummer scheme. This instance is the report payload inside the SBR Digipoort aanleverbericht.
Validation checklist
- All ledger lines classified to exactly one rubriek; AI asked about anything ambiguous (no invented amounts or OB-nummers)
-
link:schemaRefpoints to the active NT entrypoint (NT19 /bd-rpt-ob-aangiftefor tax year 2026) - Single context with the correct tijdvak
startDate/endDate; entity identifier is the OB-nummer under the omzetbelastingnummer scheme - EUR unit declared; every monetary fact has
contextRef,unitRef, anddecimals="0"(whole euros) - 1a/1b carry both omzet and btw facts; output VAT recomputed (21% / 9%) per rate
- Rubriek 5a = sum of output VAT (1a+1b+1c+1d+2a+4a+4b); 5c = 5a − 5b; 5g = 5c − 5d ± corrections
- Rubriek 3b ties to the period's ICP-opgaaf total
- KOR reduction (5d) applied only if the entrepreneur is in the scheme; suppletie handled separately
- XML well-formed, namespaces declared, prefixes consistent
- Return filed and paid by the last day of the month after the tijdvak; instance validated against the NT and Belastingdienst rubriek rules before Digipoort submission
Last updated: 2026-06-13 — verify the active Nederlandse Taxonomie (NT) version, the bd-rpt-ob-aangifte entrypoint concepts, rubriek definitions, and Digipoort/SBR submission rules against the current Belastingdienst specification before use.