The complete structure of a UBL invoice explained step by step: from header and parties to invoice lines and VAT totals.
A UBL invoice is built from a fixed set of building blocks. Whether you send invoices through the eConnect platform, via the PSB API, or are building your own integration: it helps enormously to know which components a UBL invoice contains and in what order they appear. This article walks through the structure from start to finish.
A UBL Invoice (version 2.1) consists of four main areas, always in this order:
Within each main area, elements follow a fixed XML order. This order is prescribed by the UBL standard. If you swap the order, the invoice will be rejected during validation.
Every invoice starts with a handful of mandatory elements that identify the invoice as a whole:
<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>
<cbc:ID>F-2026-00042</cbc:ID>
<cbc:IssueDate>2026-03-08</cbc:IssueDate>
<cbc:DueDate>2026-04-07</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
The CustomizationID tells the receiver which profile the invoice follows (for example NLCIUS or BIS Billing V3). The ProfileID identifies the business process. The InvoiceTypeCode indicates whether it is a regular invoice (380), a credit note variant (381) or a self-billing invoice (389).
Tip: eConnect automatically recognises the profile based on the CustomizationID. You do not need to configure anything for this.
After the identification come optional references. The most common is the order number (OrderReference), but you can also reference a contract, project or previously sent invoice:
<cac:OrderReference>
<cbc:ID>PO-2026-1234</cbc:ID>
</cac:OrderReference>
<cac:ContractDocumentReference>
<cbc:ID>CT-2025-5678</cbc:ID>
</cac:ContractDocumentReference>
For receiving organisations that process invoices automatically (think large corporates and government bodies), the order number is often mandatory. Without an order number, the invoice will not be accepted.
Many invoices contain a PDF as a visual attachment. This is included as a base64-encoded file in the AdditionalDocumentReference element. eConnect can automatically add this attachment when sending.
A UBL invoice has at least two parties, and optionally a third and fourth.
The sender of the invoice. Mandatory data includes the name, address and at least one fiscal identification number (VAT number or Chamber of Commerce number). In the Netherlands, the Chamber of Commerce number is the primary Peppol ID:
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="0106">12345678</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>Voorbeeldbedrijf B.V.</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Kerkstraat 1</cbc:StreetName>
<cbc:CityName>Woerden</cbc:CityName>
<cbc:PostalZone>3441 AB</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>NL</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>NL123456789B01</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Voorbeeldbedrijf B.V.</cbc:RegistrationName>
<cbc:CompanyID schemeID="0106">12345678</cbc:CompanyID>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
The receiver of the invoice. The structure is identical to that of the supplier. The EndpointID is the Peppol address to which the invoice is routed.
In some cases there are additional parties:
This block describes how the invoice should be paid. It contains the payment method (credit transfer, direct debit), the bank account number and optionally a payment reference:
<cac:PaymentMeans>
<cbc:PaymentMeansCode>30</cbc:PaymentMeansCode>
<cbc:PaymentID>RF71 2348 2367</cbc:PaymentID>
<cac:PayeeFinancialAccount>
<cbc:ID>NL91ABNA0417164300</cbc:ID>
<cbc:Name>Voorbeeldbedrijf B.V.</cbc:Name>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
Code 30 stands for credit transfer, 49 for direct debit, 58 for SEPA direct debit. The complete code list is defined in UNCL4461.
A free text field describing payment conditions. Some systems also use this for early payment discount (skonto).
Optional. Contains the delivery date and delivery address, relevant for goods deliveries.
Before the VAT totals come any invoice-level discounts or surcharges. Each item has an indicator (true = charge, false = allowance), an amount and a VAT category:
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:AllowanceChargeReasonCode>95</cbc:AllowanceChargeReasonCode>
<cbc:AllowanceChargeReason>Discount</cbc:AllowanceChargeReason>
<cbc:Amount currencyID="EUR">50.00</cbc:Amount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>21.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:AllowanceCharge>
AllowanceCharge can also appear within the Price element of an invoice line, to show the difference between the gross price and the net unit price. At that price level, a restriction applies: only allowances (ChargeIndicator=false) are permitted, not charges. Additionally, validation rule BR-28 requires that the gross price must not be negative. More details can be found in the article on allowances and charges.
VAT is summarised per rate. Each rate gets its own TaxSubtotal with the taxable amount and the calculated VAT amount:
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">42.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">200.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">42.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>21.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
The VAT category S stands for the standard rate. Other commonly used codes are Z (zero rate), E (exempt), AE (domestic reverse charge), K (intra-Community) and G (export).
The closing piece of the invoice. This is where all amounts that together form the payable amount are listed:
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">250.00</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">200.00</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">242.00</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount currencyID="EUR">50.00</cbc:AllowanceTotalAmount>
<cbc:PayableAmount currencyID="EUR">242.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
The calculation is: LineExtensionAmount (sum of all line amounts) minus AllowanceTotalAmount (allowances) plus ChargeTotalAmount (charges) = TaxExclusiveAmount. Add VAT on top = TaxInclusiveAmount. Minus any prepaid amount (PrepaidAmount) = PayableAmount. This calculation must be exactly correct, otherwise validation will fail.
Every invoice has at least one line. A line contains a sequence number, the invoiced quantity, the line amount, an item description and VAT information:
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="EA">10</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">250.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Consultancy hours March 2026</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>21.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">25.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
An invoice line can also contain an allowance or charge (via a nested AllowanceCharge element), reference an order line, and include additional item data such as item numbers or accounting information.
Technical: the
AccountingCostfield on the invoice line is often used to pass a cost centre or dimension. Receiving systems such as AFAS, Unit4 and SAP can read this field for automatic posting. A special application is the Reference Chart of Accounts (RGS): by including an RGS reference number inAccountingCost, the receiving accounting software can automatically post the amount to the correct ledger account. The PSB transports theAccountingCostelement unchanged in the UBL.
The strength of a UBL invoice lies in its internal consistency. The VAT totals must exactly match the VAT per line. The invoice totals must be exactly the sum of the line amounts minus allowances plus charges. If a single cent is off, the invoice will be rejected during validation.
eConnect automatically validates every invoice upon submission and provides a clear error message in case of discrepancies. Where possible, minor deviations (such as a missing field that can be derived from context) are automatically repaired.
eConnect provides a clear error message indicating which element is incorrect. Common causes include calculation errors in VAT totals, missing mandatory fields or invalid codes. Minor deviations (such as a derivable missing field) are automatically repaired where possible.
That depends on how you invoice. If you create invoices in the eConnect platform, all mandatory elements are filled in automatically. When submitting via the API, you need to provide the required fields yourself, but eConnect validates everything and gives clear feedback on missing or incorrect data.
Yes. Each invoice line has its own VAT category and percentage. In the TaxTotal block, the rates are automatically grouped into separate TaxSubtotals. This way you can combine lines with 21%, 9%, 0% or exempt VAT on a single invoice.
Want to understand a specific part of the invoice better? The articles below go deeper into common scenarios.
Tip: use the free eConnect Validator to check your invoice before you send it. The validator checks not only the schema structure, but also all calculation rules and code lists.
Validate your invoice