UBL Format Guide: Understanding the Peppol Invoice XML Structure
Every Peppol e-invoice is a UBL XML document. UBL (Universal Business Language) is the XML-based standard that defines how invoices, credit notes, and other business documents are structured for exchange across the Peppol network. Whether you are debugging a validation error, building an ERP integration, or simply want to understand what happens under the hood, this guide walks you through the complete UBL invoice structure with practical examples.

Skip the XML. Send JSON instead.
The e-invoice.be API accepts simple JSON and handles UBL conversion, EN16931 validation, and Peppol delivery automatically. You never have to write XML yourself.
View API documentationIn this article
1. What is UBL?
UBL stands for Universal Business Language. It is an XML-based standard maintained by OASIS (Organization for the Advancement of Structured Information Standards) for electronic business documents. Think of it as a universal vocabulary for invoices, credit notes, purchase orders, and other commercial documents.
Before UBL, every company and software vendor had their own format for invoices. A Belgian company using one ERP could not automatically exchange invoices with a German company using a different ERP. UBL solves this by defining a single, standardized XML schema that everyone agrees on.
The current version is UBL 2.1, which is the version used by Peppol. It covers over 60 document types, but for e-invoicing, you only need to know about a handful: invoices, credit notes, and debit notes.
2. UBL in the Peppol network
Peppol does not use raw UBL. It uses a specific profile called Peppol BIS Billing 3.0, which is built on top of UBL 2.1 and compliant with the European standard EN16931. This means every Peppol invoice must satisfy three layers of rules:
UBL 2.1 XML schema
The document must be valid XML that conforms to the UBL 2.1 schema definition. This covers the structure: which elements exist, their order, and their data types.
EN16931 business rules
The European standard defines semantic rules. For example: the total tax amount must equal the sum of all tax subtotals, and every invoice line must have a price and quantity.
Peppol BIS Billing 3.0 rules
Peppol adds its own rules on top. For example: the document must include a specific CustomizationID and ProfileID, and certain fields that are optional in EN16931 become mandatory in Peppol.
The full document type identifier for a Peppol invoice looks like this:
urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1This identifier tells the receiving system: "This is a UBL Invoice v2, compliant with EN16931:2017, following Peppol BIS Billing 3.0, using syntax version 2.1."
3. UBL invoice structure (with XML examples)
A UBL invoice has six main sections. Let's walk through each one with real XML examples that you can use as reference.
3.1 Header information
The header contains the invoice number, dates, currency, and references. It also includes the CustomizationID and ProfileID that identify this as a Peppol BIS Billing 3.0 document.
<?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">
<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>INV-2026-001</cbc:ID>
<cbc:IssueDate>2026-03-01</cbc:IssueDate>
<cbc:DueDate>2026-03-31</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:Note>Payment within 30 days</cbc:Note>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>PO-2026-0042</cbc:BuyerReference>
</Invoice>The InvoiceTypeCode 380 means a standard commercial invoice. For credit notes, you use a different root element (CreditNote) with type code 381. The BuyerReference is mandatory in Peppol and typically contains the purchase order number or a reference agreed between buyer and seller.
3.2 Party information (seller and buyer)
Every UBL invoice must identify both the seller (AccountingSupplierParty) and the buyer (AccountingCustomerParty). This includes names, addresses, VAT numbers, and optionally Peppol endpoint identifiers.
<!-- Seller -->
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="0208">0123456789</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>Acme BVBA</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Koningsstraat 1</cbc:StreetName>
<cbc:CityName>Brussels</cbc:CityName>
<cbc:PostalZone>1000</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>Acme BVBA</cbc:RegistrationName>
<cbc:CompanyID>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>Beta NV</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Meir 50</cbc:StreetName>
<cbc:CityName>Antwerp</cbc:CityName>
<cbc:PostalZone>2000</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>BE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>BE0987654321</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Beta NV</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>The EndpointID with schemeID="0208" is the Peppol participant identifier for Belgian companies. It uses the KBO/BCE enterprise number. For other countries, different scheme IDs apply. Some companies, particularly in retail and healthcare, use a GLN (scheme 0088) instead. Read more in our guide on Peppol Participant Identifiers.
3.3 Payment information
The payment section specifies how the buyer should pay. It includes the payment means code, a payment reference, and optionally the seller's bank account details.
<cac:PaymentMeans>
<cbc:PaymentMeansCode>30</cbc:PaymentMeansCode>
<cbc:PaymentID>INV-2026-001</cbc:PaymentID>
<cac:PayeeFinancialAccount>
<cbc:ID>BE68539007547034</cbc:ID>
<cbc:Name>Acme BVBA</cbc:Name>
<cac:FinancialInstitutionBranch>
<cbc:ID>BBRUBEBB</cbc:ID>
</cac:FinancialInstitutionBranch>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:PaymentTerms>
<cbc:Note>Net 30 days</cbc:Note>
</cac:PaymentTerms>Payment means code 30 indicates a credit transfer (bank wire). Code 58 is for SEPA credit transfer, which is more specific and commonly used in Europe. The PaymentID is the structured payment reference that appears on the bank statement, making reconciliation easier.
3.4 Invoice lines
Each product or service on the invoice is an InvoiceLine. It contains the item description, quantity, unit price, and the applicable tax category. The line extension amount is the quantity multiplied by the unit price.
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="C62">2</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">500.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Web development services - March 2026</cbc:Description>
<cbc:Name>Web Development</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">250.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>The unitCode attribute uses UN/ECE Recommendation 20 codes. C62 means "unit" (a generic piece/item). See the full unit code reference in section 5 below. The tax category S with 21% is the Belgian standard VAT rate.
3.5 Tax information
The TaxTotal section provides the total tax amount and a breakdown per tax category and rate. If your invoice has lines with different VAT rates, each rate gets its own TaxSubtotal.
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">105.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">500.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">105.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>The TaxAmount at the top level must equal the sum of all TaxSubtotal amounts. This is one of the EN16931 business rules that validators check. Getting this wrong is one of the most common validation errors.
3.6 Monetary totals
The LegalMonetaryTotal summarizes all amounts on the invoice. It includes the total of all line amounts, the tax-exclusive total, the tax-inclusive total, and the payable amount.
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">500.00</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">500.00</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">605.00</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="EUR">605.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>These amounts must be mathematically consistent. LineExtensionAmount is the sum of all InvoiceLine amounts. TaxExclusiveAmount is LineExtensionAmount plus any charges minus any allowances. TaxInclusiveAmount is TaxExclusiveAmount plus the total tax. PayableAmount is what the buyer actually needs to pay (after any prepaid amounts are subtracted).
4. VAT category codes
Every invoice line and tax subtotal must reference a VAT category code. These codes tell the recipient which tax treatment applies. Here are all the codes defined in the Peppol BIS Billing 3.0 specification.
| Code | Description | When to use |
|---|---|---|
S | Standard rate | Most common. Used for regular domestic invoices with a VAT percentage (e.g., 21% in Belgium, 19% in Germany). |
Z | Zero rated goods | Goods taxed at 0% rate. Different from exempt: you still report the transaction. |
E | Exempt from tax | Services or goods that are exempt from VAT by law (e.g., medical services, education). |
AE | VAT Reverse Charge | Intra-EU B2B transactions where the buyer accounts for VAT. This is the most common code for cross-border EU invoices. |
K | VAT exempt for EEA intra-community supply | Supply of goods within the EEA where the supply is exempt from VAT. |
G | Free export item, VAT not charged | Exports to countries outside the EU. |
O | Services outside scope of tax | Transactions that fall entirely outside the scope of VAT (e.g., some government fees). |
L | Canary Islands general indirect tax (IGIC) | Specific to Canary Islands tax regime. |
M | Tax for Ceuta and Melilla (IPSI) | Specific to Ceuta and Melilla tax regime. |
B | Transferred (VAT), Italy | Specific to Italian split payment mechanism. |
Practical tip
For regular domestic invoices, use S with the applicable VAT percentage (21%, 12%, 6% in Belgium). For invoices to another EU country's business, use AE (VAT Reverse Charge) with 0% VAT. These two codes cover 95% of all Peppol invoices.
5. Unit codes
Every invoice line needs a unit code in the InvoicedQuantity attribute. These come from UN/ECE Recommendation 20. Here are the most commonly used codes for e-invoicing.
| Code | Unit | Example |
|---|---|---|
C62 | Unit / piece | Generic items, licenses, subscriptions |
HUR | Hour | Consulting, development, support services |
DAY | Day | Daily rates, rentals |
MON | Month | Monthly subscriptions, retainers |
WEE | Week | Weekly rates |
MIN | Minute | Telecom services, per-minute billing |
KGM | Kilogram | Goods sold by weight |
LTR | Litre | Liquids, fuel |
MTR | Metre | Cable, fabric, construction materials |
KWH | Kilowatt hour | Electricity, energy services |
For most service-based businesses, C62 (unit) and HUR (hour) are the only codes you need. The full list contains hundreds of codes, but these ten cover the vast majority of use cases.
6. Document types
UBL defines multiple document types for different business scenarios. The Peppol network supports a growing list, but these are the most relevant for e-invoicing.
| Document type | Description | UBL root element |
|---|---|---|
| Invoice | Standard commercial invoice requesting payment | Invoice |
| Credit Note | Corrects or cancels a previously issued invoice | CreditNote |
| Self-Billing Invoice | Invoice created by the buyer on behalf of the seller | Invoice (type code 389) |
| Order | Purchase order from buyer to seller | Order |
| Order Response | Seller's response to a purchase order | OrderResponse |
| Despatch Advice | Shipping notification from seller to buyer | DespatchAdvice |
For the Belgian e-invoicing mandate, you primarily need Invoice and Credit Note. Self-billing invoices are used in specific scenarios where the buyer creates the invoice. Read our guide on self-billing for details.
7. Attachments
You can embed files in a UBL invoice using the AdditionalDocumentReference element. The file content is base64-encoded and included directly in the XML. Common attachments include a PDF version of the invoice, timesheets, contracts, or delivery receipts.
<cac:AdditionalDocumentReference>
<cbc:ID>ATT-001</cbc:ID>
<cbc:DocumentDescription>Invoice PDF</cbc:DocumentDescription>
<cac:Attachment>
<cbc:EmbeddedDocumentBinaryObject
mimeCode="application/pdf"
filename="INV-2026-001.pdf">
JVBERi0xLjQKMSAwIG9iago8PC9UeXBlIC9DYXRhbG9n...
</cbc:EmbeddedDocumentBinaryObject>
</cac:Attachment>
</cac:AdditionalDocumentReference>The mimeCode tells the recipient what type of file it is. Common values are application/pdf, image/png, and image/jpeg. Keep in mind that attachments increase the document size. Peppol has a maximum message size of approximately 100 MB, but most Access Points recommend keeping it under 10 MB for reliable delivery.
8. Validation rules
Before a UBL invoice can be sent via Peppol, it must pass three layers of validation. If any layer fails, the document is rejected and never delivered.
XML Schema Validation
Checks that the document is well-formed XML and conforms to the UBL 2.1 schema. This catches structural errors like missing required elements, wrong element order, or invalid data types.
EN16931 Schematron Validation
Checks business rules defined by the European standard. For example: mathematical consistency of amounts, valid currency codes, mandatory fields for specific VAT categories, and correct use of tax codes.
Peppol Validation Artifacts
Checks Peppol-specific rules that go beyond EN16931. This includes the correct CustomizationID and ProfileID, country-specific requirements, and additional constraints on code lists.
Common validation errors include:
- Tax amounts that don't add up (the TaxTotal must equal the sum of all TaxSubtotals)
- Missing BuyerReference (mandatory in Peppol even though optional in EN16931)
- Invalid VAT category and percentage combinations (e.g., category AE with a non-zero percentage)
- Line amounts that don't match quantity times unit price
- Missing or invalid EndpointID on parties
The e-invoice.be API validates all three layers automatically before sending. If something is wrong, you get a clear error message explaining exactly which rule failed and how to fix it. You can also use our Peppol Invoice Viewer to visually inspect and validate UBL documents.
9. The easy way: skip UBL entirely
Understanding UBL is valuable for debugging and integration work. But you do not have to write UBL XML yourself. The e-invoice.be API accepts a simple JSON structure and converts it to a fully validated Peppol BIS Billing 3.0 UBL document automatically.
Here is what the same invoice looks like as a JSON API call:
POST https://api.e-invoice.be/api/documents/
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"document_type": "INVOICE",
"invoice_id": "INV-2026-001",
"invoice_date": "2026-03-01",
"due_date": "2026-03-31",
"currency": "EUR",
"vendor_name": "Acme BVBA",
"vendor_tax_id": "BE0123456789",
"vendor_address": "Koningsstraat 1, 1000 Brussels, BE",
"customer_name": "Beta NV",
"customer_tax_id": "BE0987654321",
"customer_peppol_id": "0208:0987654321",
"items": [
{
"description": "Web Development",
"quantity": 2,
"unit_price": 250.00,
"tax_rate": 21
}
]
}That is it. No XML namespaces, no CustomizationID, no TaxScheme elements. The API handles the conversion, validates against all three rule sets, and sends the invoice via Peppol. If the recipient is not on Peppol, you get a clear error before any charge applies.
Read our full API integration guide for code examples in Python, TypeScript, Java, PHP, and Ruby.
10. Frequently asked questions
What is UBL and why is it used for e-invoicing?
UBL (Universal Business Language) is an XML-based standard maintained by OASIS for electronic business documents. It is the required format for Peppol e-invoicing in Europe. UBL defines a structured way to represent invoices, credit notes, and other documents so they can be exchanged between different systems without manual re-entry.
What version of UBL does Peppol use?
Peppol uses UBL 2.1 as defined by OASIS. Specifically, Peppol BIS Billing 3.0 requires documents that comply with the EN16931 European standard and use UBL 2.1 syntax. The full document type identifier is: urn:oasis:names:specification:ubl:schema:xsd:Invoice-2.
Do I need to write UBL XML manually?
No. Most Peppol Access Points, including e-invoice.be, accept invoice data in simpler formats like JSON and convert it to valid UBL XML automatically. You only need to understand UBL if you want to work with the raw XML directly or debug validation errors.
What is EN16931 and how does it relate to UBL?
EN16931 is the European standard for the semantic data model of electronic invoices. It defines what fields an e-invoice must contain. UBL is one of the two syntaxes (the other being CII) that can represent an EN16931-compliant invoice. Peppol chose UBL as its syntax. You can view CII invoices with our free ZUGFeRD Viewer at e-invoice.be/zugferd-viewer.
What are the main sections of a UBL invoice?
A UBL invoice has six main sections: header information (invoice number, dates, references), party information (seller and buyer details), payment information (payment terms and bank details), invoice lines (products or services), tax information (VAT breakdown), and monetary totals (summary amounts).
What VAT category codes are used in UBL invoices?
The most common VAT category codes are: S (standard rate), Z (zero rated), E (exempt from tax), AE (VAT reverse charge for intra-EU B2B), K (VAT exempt for EEA intra-community supply), G (free export, VAT not charged), and O (services outside scope of tax).
How are UBL invoices validated?
UBL invoices go through three validation layers: XML Schema validation (checks the document structure), Schematron validation (checks EN16931 business rules), and Peppol validation artifacts (checks Peppol-specific rules). The e-invoice.be API handles all three layers automatically.
Can I attach files like PDFs to a UBL invoice?
Yes. UBL supports attachments via the AdditionalDocumentReference element. Files are base64-encoded and embedded in the XML with a MIME type and filename. Common attachments include PDF copies of the invoice, timesheets, or delivery notes.
Further resources
- Peppol BIS Billing 3.0 documentation (official Peppol specification)
- OASIS UBL 2.1 standard (full UBL specification)
- E-Invoicing API integration guide (send invoices with JSON, not XML)
- Self-billing guide (how self-billing works with Peppol)
- Peppol Invoice Viewer (visualize and validate UBL documents)
Ready to send Peppol e-invoices?
Skip the UBL complexity. Create an account, send JSON, and we handle the rest. Validation, conversion, and Peppol delivery included. Pay per invoice, no subscription.