A UBL invoice in detail¶
The previous page used a deliberately small invoice to show the
shape. This page does the opposite: it takes the official OASIS UBL 2.1 example
invoice — UBL-Invoice-2.1-Example.xml, ~490 lines — and walks it top to bottom,
explaining every block. It is the best specimen to learn from precisely because it
is maximal: it exercises almost every optional structure a UBL invoice can carry,
and — as we will check at the end — its amounts reconcile exactly.
Source
The document is the published example shipped with the UBL 2.1 standard:
UBL-Invoice-2.1-Example.xml
(OASIS). It dates from 2009 and predates EN16931, so it is richer
than the EN16931 core and not EN16931-valid as-is (see the
last section). That makes it ideal for seeing the
full vocabulary; the profiles narrow from here, they do not add.
The overall shape¶
Read top to bottom, a UBL invoice is five bands: header, references, parties, monetary/payment context, and lines. The totals sit just before the lines.
flowchart TD
H["Header — who/when/what currency"] --> R["References — order, contract, attachments"]
R --> P["Parties — supplier, customer, payee"]
P --> D["Delivery & payment — where, how, terms"]
D --> A["Document allowances/charges"]
A --> T["TaxTotal — VAT broken down by category"]
T --> M["LegalMonetaryTotal — the reconciliation"]
M --> L["InvoiceLine ×5 — the actual items"]
Throughout, remember the rule from Anatomy:
cbc: is a single value, cac: is a container.
The header¶
UBLVersionID— which UBL release the document follows (2.1). Distinct from the specification identifier EN16931 adds; this one is about the syntax version itself.ID— the invoice number (EN16931 BT-1).TOSL108here.InvoiceTypeCode—380= "commercial invoice", from code list UNCL1001. Note thelistID/listAgencyIDattributes: they name which code list the value comes from and who maintains it. You will see this attribute pattern on every coded field — it is how UBL points at a code list inline.TaxPointDate— the date the VAT becomes chargeable, which can differ from the issue date.AccountingCost— a buyer's cost-accounting reference, carried through so the receiver can book the invoice automatically.
Code-list attributes are metadata, not values
listID="ISO 4217 Alpha" does not change the value EUR — it annotates it.
Validators mostly key off the element value against the bound
.gc list; the attributes document provenance.
EN16931 and Peppol tighten which attributes are allowed.
References — tying the invoice to its paperwork¶
An invoice rarely stands alone; this block links it to the surrounding documents.
InvoicePeriod— the billing period this invoice covers.OrderReference— the purchase order it answers (123). The lines later point at individual order lines viaOrderLineReference.ContractDocumentReference— the governing contract.
Then two attachments, showing UBL's two ways to carry a supporting document — by link and by value:
- By reference — an
ExternalReference/URIpointing at a file hosted elsewhere. - By value — the file itself, Base64-encoded inside
EmbeddedDocumentBinaryObject, with amimeCodesaying it is a PDF. This is how a Peppol invoice ships, say, a PDF rendering inside the XML.
The parties¶
UBL distinguishes roles, not just "sender" and "receiver". This invoice carries three: the supplier, the customer, and a separate payee.
Supplier — the full anatomy of a party¶
The seller block is the richest; every other party is a subset of this shape.
(Abridged — the original also carries Postbox, Department, CountrySubentity
and a RegistrationAddress.)
EndpointID— the party's routable electronic address;schemeID="GLN"says it is a Global Location Number. This is the field Peppol makes mandatory so the network can deliver the document. TheschemeIDnames the identifier scheme.PartyIdentification— a business identifier for the party.schemeID="ZZZ"is the UBL convention for "mutually agreed / unspecified scheme".PostalAddress— the address as separate typed fields, with the country as an ISO 3166-1 code, not free text. Structured so software can read it.PartyTaxScheme— the party's VAT registration: the VAT number (CompanyID) under a namedTaxScheme(VAT). This is what the tax rules cross-check.PartyLegalEntity— the legal identity, which can differ from the trading name: the registered company name and a company-registry number (CVRis the Danish business register).Contact— a contact point (phone, email).Person— a named individual at the party.
One shape, reused everywhere
cac:Party is a single reusable structure. The supplier, customer, payee,
delivery party — all use the same element vocabulary. Learn it once here and the
rest of the document's parties read instantly.
Customer¶
The buyer (cac:AccountingCustomerParty) has the identical structure — endpoint,
identification, name, address (in Belgium, BE), tax scheme, legal entity,
contact, person. Nothing new to learn; same shape, different data.
Payee — why a third party?¶
PayeeParty— the party to be paid, when that differs from the seller (factoring, a parent company, a collections agent). Its presence is exactly why UBL models roles separately: "who sold" and "who gets the money" are not always the same entity.
Delivery, payment, and terms¶
Delivery— when and where the goods went, separate from the buyer's billing address.PaymentMeans— how to pay.PaymentMeansCode31(UNCL4461) = credit transfer;PaymentDueDateis the deadline;PaymentIDis the reference the payer should quote.PayeeFinancialAccount— the bank account (IBAN-styleID) and its institution (a BIC underFinancialInstitution).PaymentTerms— free-text or structured terms; here a late-payment penalty.
Document-level allowances and charges¶
Before the lines, the invoice records adjustments that apply to the whole document rather than one line:
ChargeIndicator=true→ this is a charge (adds to the total): a 100 EUR packing cost.ChargeIndicator=false→ this is an allowance (a deduction): a 100 EUR promotion discount.
One element, two meanings — ChargeIndicator decides
UBL uses the same cac:AllowanceCharge element for both surcharges and
discounts. The boolean ChargeIndicator is the only thing that distinguishes
them. Read it first, always. The same element reappears at line level and even
at price level below.
TaxTotal — VAT broken down by category¶
The tax block states one grand total, then a subtotal per tax category — the heart of correct VAT reporting.
- Grand total VAT for the document: 292.20 EUR.
- Category
S(standard rate),Percent20: a taxable base of 1460.5 → 292.1 tax. The category codeScomes from UNCL5305. - Category
E(exempt), with aTaxExemptionReasonCodeand human reason. Exempt lines must say why — a grammar cannot enforce that, but Schematron can.
The three categories present here:
| Category | Code | Rate | Taxable base | VAT |
|---|---|---|---|---|
| Standard | S |
20% | 1460.5 | 292.1 |
| Reduced | AA |
10% | 1.0 | 0.1 |
| Exempt | E |
0% | −25.0 | 0.0 |
| Total | 1436.5 | 292.2 |
We will see in a moment that each taxable base equals the sum of the line amounts in that category — the numbers are not arbitrary.
LegalMonetaryTotal — the reconciliation¶
This block is where everything must add up. Each amount is a distinct concept:
LineExtensionAmount— sum of all line net amounts. Must equal Σ lines (the EN16931 rule BR-CO-10).TaxExclusiveAmount— the net total VAT is calculated on. Here the document-level allowance (−100) and charge (+100) cancel, so it equals the line total.TaxInclusiveAmount— net plus VAT.AllowanceTotalAmount/ChargeTotalAmount— the sums of the document-level allowances and charges from the block above.PrepaidAmount— already paid (1000), so it is subtracted from what is due.PayableRoundingAmount— a small rounding adjustment (0.30).PayableAmount— the bottom line: what the customer actually pays, 729 EUR.
The chain, as EN16931 expresses it:
Payable = Tax-inclusive − Prepaid + Rounding = 1729 − 1000 + 0 → 729 EUR
(and Tax-inclusive 1729 ≈ Tax-exclusive 1436.5 + VAT 292.2 = 1728.7, with the 0.30 captured as the rounding amount).
The invoice lines¶
Five lines, and they are not all positive — two are returns (negative quantity and amount), modelling credits inline. Line 1 is the fully-loaded example:
ID— line number within the invoice (not the product code).InvoicedQuantitywithunitCode="C62"—C62(UN/ECE Rec 20) is the code for "one / piece". The line net isLineExtensionAmount= 1273.OrderLineReference— links this line back to a line of the purchase order.- Line-level allowance — a 12 EUR discount for damage, scoped to this line.
- Line-level
TaxTotal— 254.6 = 1273 × 20%. Item— what is being sold: a description plus a short name.StandardItemIdentification— a global product id;schemeID="GTIN"= barcode number. (SellersItemIdentificationabove is the seller's own code.)CommodityClassification— a category code from a published taxonomy (UNSPSC; the line also carries aCPVone). For procurement analytics.ClassifiedTaxCategory— the VAT category for this item (S, 20%). Note the namespace trick: the element redeclares the default namespace to thecacURI, soClassifiedTaxCategoryand itsTaxSchemechild live in thecacnamespace without a prefix — exactly equivalent to writingcac:ClassifiedTaxCategory. A good reminder that namespaces are about the URI, not the prefix.AdditionalItemProperty— arbitrary name/value attributes ("Color: black").Price— the unit price (1273 perBaseQuantityof 1), as distinct from the line total in (2).- Price-level allowance — a contract discount expressed as a factor (0.15)
off a
BaseAmount(1500), i.e. how the net unit price was derived.
The other four lines reuse this vocabulary with less decoration:
| Line | Item | Qty | Net amount | VAT cat |
|---|---|---|---|---|
| 1 | Laptop computer | 1 | 1273.00 | S (20%) |
| 2 | Returned book | −1 | −3.96 | AA (10%) |
| 3 | "Computing for dummies" | 2 | 4.96 | AA (10%) |
| 4 | Returned IBM 5150 | −1 | −25.00 | E (exempt) |
| 5 | Network cable | 250 | 187.50 | S (20%) |
Negative lines are credits in place
Lines 2 and 4 have negative quantities and amounts — returns folded into the same invoice. UBL does not need a separate credit note for this; the line arithmetic simply goes negative and flows into the totals like any other line.
It all reconciles¶
This is why the example is worth studying: the numbers are consistent top to bottom. Two checks anyone can run.
Lines sum to the document line total (EN16931 BR-CO-10):
1273.00 + (−3.96) + 4.96 + (−25.00) + 187.50 = 1436.5
= LegalMonetaryTotal/LineExtensionAmount ✓
Each tax category's base is the sum of its lines:
| Category | Lines | Σ line net | = TaxSubtotal base |
|---|---|---|---|
| S (20%) | 1, 5 | 1273 + 187.5 | 1460.5 ✓ |
| AA (10%) | 2, 3 | −3.96 + 4.96 | 1.0 ✓ |
| E (0%) | 4 | −25 | −25.0 ✓ |
And the per-line VAT amounts sum to the grand total: 254.6 − 0.396 + 0.496 + 0 + 37.5 = 292.2 ✓. These cross-totals are exactly the constraints the EN16931 Schematron checks — here you can see them hold by hand.
How it relates to EN16931¶
This raw UBL example is a superset of what a modern e-invoice carries, and it is not EN16931-valid as it stands:
- It has no
cbc:CustomizationID— so rule BR-01 fails immediately. A conforming invoice must declare its specification identifier (see Anatomy and Peppol profiles). - It uses free-text
listIDvalues like"UN/ECE 1001 Subset"that EN16931 / Peppol replace with fixed, validated code lists. - It carries elements EN16931 does not use; a CIUS narrows this vocabulary down to the agreed core.
That is the right way to read it: UBL defines the full language, EN16931 picks a disciplined subset, and Peppol narrows further still. Seeing the maximal document first makes the constraints in the rest of this section concrete — every rule you meet is removing or pinning something you have now seen in the raw.
Where next¶
You have walked a complete invoice end to end. To see what enforces the consistency you just verified by hand, go to The validation pipeline; for the lists behind every coded field, Genericode code lists; and for any unfamiliar term, the Glossary.