DraftDraft for review, not yet a published standard. Registry vehicle/1.0-draft of 2026-10-24, marker vehicle/1.0. How changes are made.

Validation

Two questions, two kinds of tools: is the invoice a valid EN 16931 invoice in its syntax and profile, and does it follow VINvoice?

How the corpus was validated

Every test invoice was checked with the official validation artefacts of each format it claims: the XML schemas, the CEN EN 16931 schematron and the schematron of the national or network profile. XSD validation ran on a Java 21 runtime (Eclipse Temurin); the schematron rules ran as compiled XSLT with Saxon-HE 12.5. The artefacts were downloaded from their publishers and used unchanged. They are not redistributed here.

Results by validator
Validator Files Rules fired Failed
Factur-X 1.09.2 EN 16931 XSD 13 – 0
UN/CEFACT CII D16B XSD 14 – 0
Factur-X 1.09.2 EXTENDED XSD 1 – 0
UBL 2.1 Invoice / CreditNote XSD 5 – 0
CEN EN 16931 validation 1.3.17 19 2099 0
Factur-X 1.09.2 EN 16931 schematron 12 1514 0
Factur-X 1.09.2 EXTENDED schematron 1 138 0
KoSIT XRechnung 3.0.2 schematron 2.6.0 2 39 0
Peppol BIS Billing 3.0.20 4 172 0

“Failed” counts schema errors (XSD) and failed assertions plus reports (schematron), warnings included. “Rules fired” is the number of schematron rule contexts that matched, summed over all files.

Results per file

Failed rules per file and validator (– = not applicable to this file)
File Factur-X XSD CII D16B XSD Factur-X EXT XSD UBL 2.1 XSD CEN Factur-X Factur-X EXT XRechnung Peppol VINvoice checker
credit-25a.fx.cii.xml 00––00––– level 2
credit-25a.peppol.ubl.xml –––00–––0 level 2
export-25a.fx.cii.xml 00––00––– level 2
level0.fx.cii.xml 00––00––– level 0 · 1 info
new-intra-eu-unregistered.fx.cii.xml 00––00––– level 2
new-intra-eu.fx.cii.xml 00––00––– level 2
sale-25a-level1.fx.cii.xml 00––00––– level 1
sale-25a-xborder.peppol.ubl.xml –––00–––0 level 2
sale-25a.ext.cii.xml –00–0–0–– level 2
sale-25a.fx.cii.xml 00––00––– level 2
sale-25a.peppol.ubl.xml –––00–––0 level 2
sale-25a.xr.cii.xml 00––0––0– level 2
sale-25a.xr.ubl.xml –––00––0– level 2
sale-regular-odometer-inoperative.fx.cii.xml 00––00––– level 2
workshop.fx.cii.xml 00––00––– level 2 · 2 info
workshop.peppol.ubl.xml –––00–––0 level 2 · 2 info
neg-margin-k.fx.cii.xml 00––00––– level 2 · 4 errors · 2 warnings
neg-profile.fx.cii.xml 00––00––– level 2 · 3 errors · 4 warnings
neg-profile2.fx.cii.xml 00––00––– level 1, 2 · 4 errors · 4 warnings · 4 info

Are the validators live?

A run with zero findings proves little unless the same setup also rejects broken input. Deliberately broken copies of the test invoices were rejected as expected: the scheme VIN instead of AKG fails the CEN rule BR-CL-07; two line object identifiers on one line fail the XSD and the Peppol rule R100; two invoice notes on a cross-border Peppol invoice fail the Peppol rule R002.

Not yet run

The KoSIT validator application, the official FNFE and FeRD validators, Mustangproject, Peppol BIS 3.0.21, delivery through Peppol access points, and import into accounting and procurement systems. Results will be added here as they come in.

Reference checker

The format validators above do not know VINvoice. The reference checker reads an invoice (CII or UBL), applies the reading rules and reports where a vehicle line departs from the convention. It takes grammars, labels and the statutory flag from registry.json, so it follows the registry without code changes. It never rejects an invoice: an error means only that the line does not conform to VINvoice. The checker is a single Python file without dependencies, licensed under Apache-2.0 (checker/vehicle_check.py) . A regression test runs it on every test invoice and compares the result with the expected findings.

Schematron

The same rules are published as ISO Schematron, vinvoice.sch, generated from the registry. It runs on CII and UBL invoices in any XSLT 2.0 Schematron processor, next to the EN 16931 and national rules. Rule identifiers match the checker. The VIN check digit (V-05), the six-months-or-6,000-km test of V-08, the registration-number comparison (V-18) and the reader notes on level 0 and attached lines (V-19, V-20) stay with the checker. On the test corpus both report the same errors and warnings; continuous integration checks this on every change.

Checker rules
Rule Severity Finding
V-01error A marker vehicle.specificationId without a role row vehicle.roleCode.
V-02error A value does not match the grammar of its key and is dropped, or the marker value is malformed and the line is treated as unmarked.
V-03warning A key that is not repeatable appears twice on a line; the first value is kept.
V-04warning BT-128 and the row vehicle.vin differ; BT-128 wins.
V-05warning The VIN is not 17 characters long or its check digit fails. Older and non-EU vehicles can legitimately deviate, so this is never an error.
V-06error A supply or trade-in line with a quantity other than 1.
V-07error, warning vehicle.marginSchemeIndicator on a line with VAT category K (error); with a category other than E, F or G, without the margin-scheme note in BG-1, or together with condition new (warnings).
V-08error, warning vehicle.newMeansOfTransportIndicator without vehicle.maximumNetPowerKw or vehicle.engineCapacityCm3, or without any newness feature (errors); without an odometer reading, registered but without vehicle.firstEntryIntoServiceDate, or beyond both 6,000 km and six months (warnings).
V-09warning, info A used vehicle supplied or traded in without an odometer reading or a status saying none is available (warning; only where vehicle.categoryCode is empty or an M, N or L category); without vehicle.firstRegistrationDate (info: correct only if it was never registered).
V-10warning A service line without VIN or registration number, or without an odometer reading or status (for vehicles that have an odometer).
V-11error, info A statutory key written without its label (error); a key longer than 40 or a name longer than 64 characters, a label that is not in the registry, or a marker that is not the first row (info).
V-12error More than one BT-128 on a line.
V-13error vehicle.odometerReadingKm and vehicle.odometerReadingMi on the same line.
V-14error A row uses the measure or quantity variant of BT-161 instead of its text value.
V-15info Rows on a line without a marker are ignored.
V-16warning vehicle.batteryStateOfHealthPercent without vehicle.batteryStateOfHealthDate or without vehicle.batteryStateOfHealthMethodCode.
V-17error An odometer reading although vehicle.odometerReadingStatusCode says the reading is not available.
V-18warning A registration number in BT-128 (scheme ABZ) differs from vehicle.registrationNumber.
V-19info Level 0: a vehicle identified only by BT-128 or BT-18.
V-20info A line without a marker is attached to a vehicle through its BT-128.
V-21warning Unknown major version in the marker; the rows of that line are skipped.
V-22info Unknown key or reserved name, ignored. Vendor rows (reverse-DNS names outside vehicle.) are ignored without a finding.

Checker output for the negative controls

neg-margin-k.fx.cii.xml (6 findings)

Negative control for the checker: margin-scheme flag on an intra-EU exempt line (category K) and further faults. Valid EN 16931, not a valid VINvoice line.

Rule Severity Line Message
V-06 error 1 role supply with quantity 2; one vehicle per line
V-07 error 1 margin scheme on an intra-EU supply (category K): excluded by Art. 139(3) and Art. 313(2) Directive 2006/112/EC
V-07 warning 1 margin line but no BG-1 note with the Art. 226(14) mention (for example 'Margin scheme - Second-hand goods' or 'Gebrauchtgegenstände/Sonderregelung')
V-08 error 1 new means of transport without vehicle.maximumNetPowerKw or vehicle.engineCapacityCm3
V-08 error 1 new means of transport without any newness feature (no odometer reading, no vehicle.firstEntryIntoServiceDate)
V-09 warning 1 used supply without an odometer reading (or status inoperative/unknown)
neg-profile.fx.cii.xml (7 findings)

Negative control for the checker: malformed rows (statutory key without its label, duplicates, grammar violations).

Rule Severity Line Message
V-11 error 1 statutory key vehicle.odometerReadingKm in bare form; the labelled form is mandatory
V-03 warning 1 duplicate vehicle.make; first value kept
V-02 error 1 vehicle.odometerReadingKm='48.200 km' violates the registry grammar; value dropped by the reader
V-02 error 1 vehicle.configuration='=HYPERLINK("x")' violates the registry grammar; value dropped by the reader
V-04 warning 1 BT-128 TSTVEH016N0000001 differs from vehicle.vin TSTVEH016N0000009; BT-128 wins
V-16 warning 1 vehicle.batteryStateOfHealthPercent without vehicle.batteryStateOfHealthDate
V-16 warning 1 vehicle.batteryStateOfHealthPercent without vehicle.batteryStateOfHealthMethodCode
neg-profile2.fx.cii.xml (12 findings)

Negative control for the checker: reader edge cases (unknown major version, unknown key, both odometer units, unmarked rows, a vendor row).

Rule Severity Line Message
V-02 error 1 vehicle.firstRegistrationDate='2022-02-30' violates the registry grammar; value dropped by the reader
V-05 warning 1 VIN TSTVEH016N0000002 is not 17 ISO 3779 characters or its check digit fails
V-13 error 1 vehicle.odometerReadingKm and vehicle.odometerReadingMi on the same line
V-17 error 1 odometer reading given although vehicle.odometerReadingStatusCode = inoperative
V-09 info 1 used supply without vehicle.firstRegistrationDate (correct only if the vehicle was never registered)
V-18 warning 2 BT-128 ABZ registration number differs from vehicle.registrationNumber
V-10 warning 2 service line without odometer reading or status
V-15 info 3 vehicle rows on an unmarked line are ignored
V-21 warning 4 unknown major version vehicle/2.0; vehicle rows skipped
V-11 info 5 label 'Hersteller' not in the registry set for vehicle.make
V-22 info 5 unknown key vehicle.foo ignored
V-01 error 5 marker without vehicle.roleCode