The structure of a UBL invoice

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.

The big picture

A UBL Invoice (version 2.1) consists of four main areas, always in this order:

  1. Invoice level: identification, type, dates and references
  2. Parties: who sends, who receives, and optionally who gets paid
  3. Payment and delivery information: how and when payment is made
  4. Financial: VAT totals, invoice totals and the individual invoice lines

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.

1. Invoice level
Identification and type

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.

References

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.

Attachments

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.

2. Parties

A UBL invoice has at least two parties, and optionally a third and fourth.

The supplier (AccountingSupplierParty)

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 buyer (AccountingCustomerParty)

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.

Optional parties

In some cases there are additional parties:

  • PayeeParty: when the amount should go to a party other than the supplier (for example with factoring)
  • TaxRepresentativeParty: when a fiscal representative pays VAT on behalf of the supplier (occurs with foreign suppliers who are VAT-liable in the Netherlands)
3. Payment and delivery
Payment method (PaymentMeans)

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.

Payment terms (PaymentTerms)

A free text field describing payment conditions. Some systems also use this for early payment discount (skonto).

Delivery information (Delivery)

Optional. Contains the delivery date and delivery address, relevant for goods deliveries.

4. The financial block
Allowances and charges at invoice level (AllowanceCharge)

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 totals (TaxTotal)

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).

Invoice totals (LegalMonetaryTotal)

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.

Invoice lines (InvoiceLine)

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 AccountingCost field 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 in AccountingCost, the receiving accounting software can automatically post the amount to the correct ledger account. The PSB transports the AccountingCost element unchanged in the UBL.

Everything must add up

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.

Frequently asked questions
What if my invoice is rejected during validation?

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.

Do I need to fill in all elements of a UBL invoice myself?

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.

Can I use multiple VAT rates on a single invoice?

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