XML Invoice Format: A Complete Example and How to Validate It
A complete, valid XML invoice example in UBL 2.1. Which fields are mandatory, how UBL, CII, XRechnung and Factur-X differ, and how to validate your file.

Written by
Tom Van Asbroeck
Last updated: · 9 min read
Expert verified

An XML invoice is an invoice where every field sits inside a named tag instead of being printed on a page. The invoice number is in one tag, the issue date in another, each line item in its own block. Because every value is labelled, software reads the file directly. No OCR, no retyping, no guessing which number is the VAT. In Europe the tag names come from a standard, almost always UBL 2.1 or UN/CEFACT CII.
A few lines from a real XML invoice
<cbc:ID>INV-2026-0042</cbc:ID>
<cbc:IssueDate>2026-03-01</cbc:IssueDate>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:LegalMonetaryTotal>
<cbc:PayableAmount currencyID="EUR">3872.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>This guide gives you a complete invoice you can copy, the list of fields that are legally mandatory, the differences between the XML dialects you will meet, and the three validation layers your file has to survive before it reaches a customer.
1. A complete XML invoice example
Below is a full UBL 2.1 invoice that follows the Peppol BIS Billing 3.0 profile. This is not a trimmed illustration. It contains every element the Peppol validation rules require, so you can paste it into a file, change the values, and validate it. One seller, one buyer, one line item, 21% Belgian VAT.
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<!-- Which rulebook this invoice follows -->
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<!-- Header -->
<cbc:ID>INV-2026-0042</cbc:ID>
<cbc:IssueDate>2026-03-01</cbc:IssueDate>
<cbc:DueDate>2026-03-31</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>PO-8891</cbc:BuyerReference>
<!-- Seller -->
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="0208">0123456789</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>Webbureau Leuven BV</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Diestsestraat 12</cbc:StreetName>
<cbc:CityName>Leuven</cbc:CityName>
<cbc:PostalZone>3000</cbc:PostalZone>
<cac:Country><cbc:IdentificationCode>BE</cbc:IdentificationCode></cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>BE0123456789</cbc:CompanyID>
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Webbureau Leuven BV</cbc:RegistrationName>
<cbc:CompanyID schemeID="0208">0123456789</cbc:CompanyID>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<!-- Buyer -->
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="0208">0987654321</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>Bakkerij Peeters NV</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Grote Markt 3</cbc:StreetName>
<cbc:CityName>Antwerpen</cbc:CityName>
<cbc:PostalZone>2000</cbc:PostalZone>
<cac:Country><cbc:IdentificationCode>BE</cbc:IdentificationCode></cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Bakkerij Peeters NV</cbc:RegistrationName>
<cbc:CompanyID schemeID="0208">0987654321</cbc:CompanyID>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<!-- How the buyer should pay -->
<cac:PaymentMeans>
<cbc:PaymentMeansCode>30</cbc:PaymentMeansCode>
<cac:PayeeFinancialAccount>
<cbc:ID>BE68539007547034</cbc:ID>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<!-- VAT breakdown: one subtotal per rate -->
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">672.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">3200.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">672.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>21</cbc:Percent>
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<!-- Document totals -->
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">3200.00</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">3200.00</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">3872.00</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="EUR">3872.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<!-- Line item -->
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="HUR">40</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">3200.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Website redesign</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>21</cbc:Percent>
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">80.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>Four details in this file trip up most first attempts:
- Element order is fixed. UBL is defined by an XSD schema with a strict sequence. Putting
cac:InvoiceLinebeforecac:LegalMonetaryTotalis not a style choice. It breaks the file. CustomizationIDis the rulebook. It tells the receiver which validation rules apply. Get it wrong and the invoice is rejected by the network before a human sees it.- Every amount needs a currency attribute. A bare
3872.00withoutcurrencyIDfails schema validation. - The VAT breakdown must reconcile.
TaxSubtotal/TaxableAmounthas to equal the sum of the line net amounts at that rate. Cent rounding differences are the single most common rejection we see.
Paste this file into our Invoice Viewer to see it rendered as a readable document, or into the Peppol Validator to run the real rule set against it. For a field-by-field walkthrough of every UBL element, including VAT categories, unit codes and attachments, see our UBL invoice format guide.
2. The fields that are legally mandatory
UBL defines hundreds of optional elements. EN 16931, the European standard behind every compliant e-invoice, narrows that down to a core set that must always be present. Each field has a BT number in the standard. These are the ones you cannot leave out of a Peppol invoice.
| EN 16931 field | UBL element | Note |
|---|---|---|
| BT-24 Specification identifier | cbc:CustomizationID | Fixed string for Peppol BIS Billing 3.0 |
| BT-1 Invoice number | cbc:ID | Must be unique per seller |
| BT-2 Issue date | cbc:IssueDate | Format YYYY-MM-DD, no time component |
| BT-3 Invoice type code | cbc:InvoiceTypeCode | 380 for a commercial invoice, 381 for a credit note |
| BT-5 Currency | cbc:DocumentCurrencyCode | ISO 4217 code |
| BT-34 / BT-49 Electronic addresses | cbc:EndpointID | Peppol identifiers for seller and buyer, both required |
| BT-27 / BT-44 Legal names | cac:PartyLegalEntity/cbc:RegistrationName | The registered name, not a trading name |
| BT-40 / BT-55 Country codes | cac:Country/cbc:IdentificationCode | Two-letter ISO 3166 code on both addresses |
| BT-106 to BT-115 Totals | cac:LegalMonetaryTotal | Line total, total without VAT, total with VAT, amount due |
| BG-23 VAT breakdown | cac:TaxTotal/cac:TaxSubtotal | One subtotal per VAT rate and category |
Peppol adds two rules on top of EN 16931 that catch people out. A buyer reference or a purchase order reference is mandatory, so an invoice with neither is rejected. And both electronic addresses must be present, even though EN 16931 itself treats them as optional. If you are mapping your own data model to the standard, our EN 16931 field mapper shows the full BT-to-UBL correspondence.
3. XML invoice formats, and how to tell them apart
"XML invoice" is not one format. It is a family of incompatible schemas that all happen to use XML. If a customer sends you a file and you do not know what it is, the root element and the customization identifier tell you within seconds.
| Format | Root element | How to recognise it |
|---|---|---|
| UBL 2.1 / Peppol BIS 3.0 | <Invoice> | CustomizationID contains peppol.eu:2017:poacc:billing:3.0 |
| CII (UN/CEFACT D16B) | <rsm:CrossIndustryInvoice> | Tag prefixes rsm, ram and udt instead of cac and cbc |
| XRechnung (Germany) | <Invoice> or <rsm:CrossIndustryInvoice> | Specification identifier contains xrechnung_3.0 |
| ZUGFeRD / Factur-X | None. It is a PDF. | PDF/A-3 with an attachment named factur-x.xml or zugferd-invoice.xml |
| FatturaPA (Italy) | <p:FatturaElettronica> | A versione attribute of FPA12 or FPR12 |
UBL and CII carry the same EN 16931 data with different tag names. That means a mechanical conversion between them is possible, and it is what most Access Points do internally. XRechnung is not a third syntax. It is a national profile that narrows EN 16931 and can be expressed in either UBL or CII. FatturaPA sits outside EN 16931 entirely and needs a real transformation, not a rename.
Which format do you actually need?
In Belgium, and anywhere else on the Peppol network, the answer is UBL 2.1 with the Peppol BIS Billing 3.0 profile. A ZUGFeRD or Factur-X PDF emailed to a customer does not satisfy the Belgian B2B mandate. The invoice has to travel over Peppol as structured XML.
Not sure what you have? Drop the file into our format detector. To pull the XML out of a hybrid PDF, use the ZUGFeRD XML extractor. To move a CII file onto Peppol, use the CII to UBL converter.
4. XML vs. PDF vs. EDI
The line that matters for compliance is structured versus unstructured. A PDF looks like an invoice, but to software it is a picture of text. It has no idea which number is the total and which is the VAT.
| Method | Format | Machine-readable? | Valid under the Belgian mandate? |
|---|---|---|---|
| XML invoice over Peppol | Structured (UBL, CII) | Yes, fully | Yes |
| EDI invoice | Structured (EDIFACT, ANSI X12) | Yes, fully | Only if converted to Peppol |
| PDF invoice by email | Unstructured (visual document) | No, needs OCR | No |
| Hybrid (ZUGFeRD / Factur-X) | PDF with embedded XML | Yes, via the XML layer | Not by email |
Hybrid formats are a useful bridge during a transition. Recipients with modern software extract the XML, everyone else reads the PDF. They are widely used in Germany and France. They are not a substitute for Peppol delivery in Belgium. If you need to show an XML invoice to a human, our Peppol to PDF converter renders it as a readable document.
5. How to validate an XML invoice
A file that opens without error in a browser is not a valid invoice. Peppol invoices pass through three independent validation layers, and each one rejects different things. Most developers discover the second and third layers the hard way.
XSD schema: is the shape right?
Checks that elements exist, are spelled correctly, sit in the required order and hold the right data type. A typical failure reads cvc-complex-type.2.4.a: Invalid content was found starting with element. That almost always means an element is out of sequence.
EN 16931 Schematron: do the numbers make sense?
Business rules with BR codes. BR-16 requires at least one invoice line. BR-CO-10 requires the sum of line net amounts to equal the stated line total. BR-CO-15 requires the total with VAT to equal the total without VAT plus the VAT amount. BR-S-08 requires the standard-rate taxable amount to match the lines at that rate.
Peppol BIS Schematron: does the network accept it?
Rules with PEPPOL-EN16931 codes. R003 requires a buyer reference or a purchase order reference. R010 and R020 require the buyer and seller electronic addresses. R004 requires the exact Peppol specification identifier. These rules exist only on Peppol, so a file that is perfectly valid EN 16931 can still be rejected here.
Delivery check: can the recipient receive it?
Not validation of the file, but the last thing that stops a send. The recipient's Peppol ID has to be registered in an SMP for the invoice document type. Look the identifier up before you send with our Peppol ID checker.
Rounding is the number one rejection cause
If you calculate line totals at full float precision and round only the document total, BR-CO-10 will fail by one cent. Round each line amount to two decimals first, then sum the rounded values. Do the same for the VAT breakdown. This single change eliminates most validation failures we see from new integrations.
6. Creating XML invoices without writing XML
Hand-building UBL is possible, and the example above shows it is not mysterious. It is still rarely the right use of your time. Namespace handling, element ordering, VAT edge cases and Schematron updates are ongoing maintenance, not a one-off task. Most teams send flat data to an Access Point and let it produce and validate the XML.
With the e-invoice.be API you post plain JSON. No namespaces, no tag ordering, no Schematron.
POST https://api.e-invoice.be/api/documents/
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"document_type": "INVOICE",
"invoice_id": "INV-2026-0042",
"invoice_date": "2026-03-01",
"due_date": "2026-03-31",
"currency": "EUR",
"vendor_name": "Webbureau Leuven BV",
"vendor_tax_id": "BE0123456789",
"vendor_address": "Diestsestraat 12, 3000 Leuven, BE",
"customer_name": "Bakkerij Peeters NV",
"customer_tax_id": "BE0987654321",
"customer_peppol_id": "0208:0987654321",
"items": [
{
"description": "Website redesign",
"quantity": 40,
"unit_price": 80.00,
"tax_rate": 21.00
}
]
}That request produces exactly the UBL document shown in section 1. The API runs all three validation layers before delivery and returns the rule code if anything fails, so you fix problems before the invoice leaves your system. You can download the generated XML for your archive at any time. Incoming invoices arrive as the same flat JSON through a webhook, with the original XML attached.
If you are not a developer, you never touch any of this. Create an account, register your enterprise number, and send invoices from the dashboard or by email. The account and your Peppol ID are free. Every invoice you send or receive is billed per invoice, starting at €0.25. See the API integration guide for full code examples, or the Peppol registration guide to get set up.
7. Frequently asked questions
What is an XML invoice?
An XML invoice is an invoice where every field is stored inside a named tag instead of being printed on a page. The invoice number sits in one tag, the issue date in another, each line item in its own block. Because the meaning of every value is labelled, accounting software, ERP systems and tax authorities can read the file directly, with no OCR and no retyping. In Europe the tag names are set by a standard, usually UBL 2.1 or UN/CEFACT CII.
What does an XML invoice file look like?
It is a plain text file you can open in Notepad or any code editor. It starts with an XML declaration, then a root element such as <Invoice>, then nested elements like <cbc:ID>INV-2026-0042</cbc:ID> and <cbc:IssueDate>2026-03-01</cbc:IssueDate>. A typical Peppol invoice is 100 to 300 lines long. The full example in this article is a working Peppol BIS Billing 3.0 invoice you can copy and validate.
Which XML invoice format do I need?
In Belgium and everywhere else on the Peppol network, you need UBL 2.1 following the Peppol BIS Billing 3.0 profile. Germany also accepts XRechnung, which exists in a UBL flavour and a CII flavour. France and Germany use Factur-X and ZUGFeRD, which wrap a CII XML file inside a PDF. Italy uses its own FatturaPA schema. If you are unsure what you received, check the root element and the CustomizationID.
How do I validate an XML invoice?
Validation happens in three layers. First the XSD schema checks that the file is well formed and that elements appear in the right order. Then the EN 16931 Schematron checks the business rules, such as whether the line totals add up to the invoice total. Finally the Peppol BIS Schematron checks network specific rules, such as the requirement for a buyer reference. A file can pass the XSD and still fail the Schematron, which is the most common surprise for developers.
How do I create an XML invoice without writing XML?
Send flat JSON to an Access Point API and let it build the XML. With the e-invoice.be API you post fields like vendor_name, customer_name, invoice_id and an items array. The Access Point generates valid UBL 2.1, runs all three validation layers, and delivers the file over Peppol. You can still download the generated XML afterwards if you need it for your archive.
What is the difference between a PDF invoice and an XML invoice?
A PDF is a picture of an invoice. Software can see the pixels but does not know which number is the VAT and which is the total, so it needs OCR or manual entry. An XML invoice labels every value explicitly, so it can be booked automatically. Under the Belgian e-invoicing mandate a PDF sent by email is not a valid e-invoice, even if the PDF was generated by accounting software.
Is XML invoicing the same as e-invoicing?
XML invoicing is one form of e-invoicing, and by far the most common one in Europe. E-invoicing means any exchange of invoices in a structured format. EDI formats such as EDIFACT are structured but are not XML. Scanned invoices and PDFs are not structured at all, so they are not e-invoices in the legal sense.
Is XML invoicing mandatory in Belgium?
Since January 2026, Belgian B2B invoices must be exchanged as structured e-invoices over Peppol. The law mandates Peppol rather than XML by name, but Peppol carries UBL 2.1 XML, so in practice every compliant Belgian B2B invoice is an XML file. Your Access Point generates and validates that XML for you.
Free XML invoice tools
- Peppol Validator Run the real EN 16931 and Peppol rule set against your XML file
- Invoice Viewer View and inspect UBL XML invoices visually
- Invoice Generator Fill in a form and download a valid UBL XML invoice
- Format Detector Detect whether your XML file is UBL, CII, ZUGFeRD or Factur-X
- CII to UBL Converter Convert Cross-Industry Invoice XML to UBL format
- XRechnung Viewer View XRechnung XML invoices for the German market
Send XML e-invoices without building UBL yourself
You send the data. We generate the XML, run every validation layer, and deliver over Peppol. The account and your Peppol ID are free. Invoices are billed per invoice, from €0.25.

Tom Van Asbroeck
Co-founder, Peppol & tax expert
Tom is a co-founder of e-invoice.be and runs its operations. He helps companies and software vendors connect to Peppol and answers their questions on e-invoicing rules and VAT.
- Co-founder of e-invoice.be, a certified Peppol Access Point
- Hosts the 15-minute Peppol discovery calls
- Follows the e-invoicing mandates in Belgium, France and Germany
Moving from your own XML format to Peppol? 15 min to plan the mapping.
If you're already generating XML, the work isn't starting over, it's mapping. We'll walk through what changes for UBL/Peppol BIS 3.0 and what doesn't.
Vendor-neutral. No obligation to switch platforms.

Hosted by Tom Van Asbroeck
Peppol & tax expert