How to read the mapping
The UBL column gives paths from the root element, Invoice or CreditNote, with the usual cac: (aggregate) and cbc: (basic) prefixes. The CII column gives paths from the root rsm:CrossIndustryInvoice. CII paths get long, so three abbreviations keep the tables readable:
- Agreement stands for
rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement: the parties and the references.
- Delivery stands for
rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery: where and when the goods or services went.
- Settlement stands for
rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement: currency, payment, VAT and totals.
The authoritative source for every path is the pair of syntax bindings CEN publishes with the standard, EN 16931-3-2 for UBL and EN 16931-3-3 for CII. The tables below cover the terms that carry real data in most invoices; the bindings also list the rarely used ones.
Document header and references
| Business term | UBL | CII |
| BT-1 Invoice number | cbc:ID | rsm:ExchangedDocument/ram:ID |
| BT-2 Issue date | cbc:IssueDate | rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString (format 102) |
| BT-3 Invoice type code | cbc:InvoiceTypeCode or cbc:CreditNoteTypeCode | rsm:ExchangedDocument/ram:TypeCode |
| BT-5 Invoice currency | cbc:DocumentCurrencyCode | Settlement/ram:InvoiceCurrencyCode |
| BT-6 VAT accounting currency | cbc:TaxCurrencyCode | Settlement/ram:TaxCurrencyCode |
| BT-9 Payment due date | cbc:DueDate (Invoice), cac:PaymentMeans/cbc:PaymentDueDate (CreditNote) | Settlement/ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/udt:DateTimeString |
| BT-10 Buyer reference | cbc:BuyerReference | Agreement/ram:BuyerReference |
| BT-12 Contract reference | cac:ContractDocumentReference/cbc:ID | Agreement/ram:ContractReferencedDocument/ram:IssuerAssignedID |
| BT-13 Purchase order reference | cac:OrderReference/cbc:ID | Agreement/ram:BuyerOrderReferencedDocument/ram:IssuerAssignedID |
| BT-14 Sales order reference | cac:OrderReference/cbc:SalesOrderID | Agreement/ram:SellerOrderReferencedDocument/ram:IssuerAssignedID |
| BT-16 Despatch advice reference | cac:DespatchDocumentReference/cbc:ID | Delivery/ram:DespatchAdviceReferencedDocument/ram:IssuerAssignedID |
| BT-20 Payment terms | cac:PaymentTerms/cbc:Note | Settlement/ram:SpecifiedTradePaymentTerms/ram:Description |
| BT-22 Invoice note | cbc:Note | rsm:ExchangedDocument/ram:IncludedNote/ram:Content |
| BT-23 Business process | cbc:ProfileID | rsm:ExchangedDocumentContext/ram:BusinessProcessSpecifiedDocumentContextParameter/ram:ID |
| BT-24 Specification identifier | cbc:CustomizationID | rsm:ExchangedDocumentContext/ram:GuidelineSpecifiedDocumentContextParameter/ram:ID |
| BT-25 Preceding invoice (BG-3) | cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID | Settlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID |
| BG-14 Invoicing period | cac:InvoicePeriod/cbc:StartDate, cbc:EndDate | Settlement/ram:BillingSpecifiedPeriod/ram:StartDateTime, ram:EndDateTime |
| BT-72 Actual delivery date | cac:Delivery/cbc:ActualDeliveryDate | Delivery/ram:ActualDeliverySupplyChainEvent/ram:OccurrenceDateTime/udt:DateTimeString |
BT-24 decides which rule set a receiver applies, so it is the one value a mapping must never copy blindly between contexts. A Peppol invoice declares the Peppol BIS customization ID, a Factur-X declares its profile URN, and the same invoice moved into the other ecosystem has to declare that ecosystem's identifier.
Seller and buyer
In UBL the seller is cac:AccountingSupplierParty/cac:Party and the buyer cac:AccountingCustomerParty/cac:Party. In CII they are Agreement/ram:SellerTradeParty and Agreement/ram:BuyerTradeParty. Below each party, the structure is the same for seller and buyer, so the table lists the paths relative to the party:
| Business term (seller / buyer) | UBL, under the Party | CII, under the TradeParty |
| BT-27 / BT-44 Name | cac:PartyLegalEntity/cbc:RegistrationName | ram:Name |
| BT-28 / BT-45 Trading name | cac:PartyName/cbc:Name | ram:SpecifiedLegalOrganization/ram:TradingBusinessName |
| BT-29 / BT-46 Party identifier | cac:PartyIdentification/cbc:ID | ram:ID, or ram:GlobalID with a schemeID |
| BT-30 / BT-47 Legal registration | cac:PartyLegalEntity/cbc:CompanyID | ram:SpecifiedLegalOrganization/ram:ID |
| BT-31 / BT-48 VAT identifier | cac:PartyTaxScheme/cbc:CompanyID, TaxScheme ID VAT | ram:SpecifiedTaxRegistration/ram:ID with schemeID VA |
| BT-32 Seller tax registration | A second cac:PartyTaxScheme/cbc:CompanyID, TaxScheme ID other than VAT | ram:SpecifiedTaxRegistration/ram:ID with schemeID FC |
| BT-34 / BT-49 Electronic address | cbc:EndpointID with schemeID | ram:URIUniversalCommunication/ram:URIID with schemeID |
| BG-5 / BG-8 Postal address | cac:PostalAddress: cbc:StreetName, cbc:CityName, cbc:PostalZone, cac:Country/cbc:IdentificationCode | ram:PostalTradeAddress: ram:LineOne, ram:CityName, ram:PostcodeCode, ram:CountryID |
| BG-6 / BG-9 Contact | cac:Contact: cbc:Name, cbc:Telephone, cbc:ElectronicMail | ram:DefinedTradeContact: ram:PersonName, ram:TelephoneUniversalCommunication/ram:CompleteNumber, ram:EmailURIUniversalCommunication/ram:URIID |
Two rows cause most of the trouble. The legal name sits in PartyLegalEntity in UBL but directly on the party in CII, while the trading name goes the opposite way. And the VAT identifier is told apart from other tax numbers by the TaxScheme in UBL but by a scheme attribute in CII. The electronic addresses are what Peppol routes on; the Factur-X and ZUGFeRD to Peppol guide covers the schemes a participant can be registered under.
Payment instructions
| Business term | UBL | CII |
| BT-81 Payment means code | cac:PaymentMeans/cbc:PaymentMeansCode | Settlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:TypeCode |
| BT-83 Remittance information | cac:PaymentMeans/cbc:PaymentID | Settlement/ram:PaymentReference |
| BT-84 Payment account (IBAN) | cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID | Settlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeePartyCreditorFinancialAccount/ram:IBANID |
| BT-85 Account name | cac:PayeeFinancialAccount/cbc:Name | ram:PayeePartyCreditorFinancialAccount/ram:AccountName |
| BT-86 Payment service provider (BIC) | cac:PayeeFinancialAccount/cac:FinancialInstitutionBranch/cbc:ID | ram:PayeeSpecifiedCreditorFinancialInstitution/ram:BICID |
| BT-89 Mandate reference | cac:PaymentMeans/cac:PaymentMandate/cbc:ID | Settlement/ram:SpecifiedTradePaymentTerms/ram:DirectDebitMandateID |
The remittance information is a good example of a value that changes level: UBL keeps it on the payment means, CII on the settlement itself. An account number that is not an IBAN goes into ram:ProprietaryID instead of ram:IBANID on the CII side, while UBL uses the same cbc:ID for both.
Allowances, charges, VAT and totals
| Business term | UBL | CII |
| BG-20 / BG-21 Document allowance / charge | cac:AllowanceCharge with cbc:ChargeIndicator false / true | Settlement/ram:SpecifiedTradeAllowanceCharge with ram:ChargeIndicator/udt:Indicator false / true |
| BT-92 / BT-99 Amount | cbc:Amount | ram:ActualAmount |
| BT-97 / BT-104 Reason | cbc:AllowanceChargeReason | ram:Reason |
| BG-23 VAT breakdown | cac:TaxTotal/cac:TaxSubtotal | Settlement/ram:ApplicableTradeTax |
| BT-116 Taxable amount | cbc:TaxableAmount | ram:BasisAmount |
| BT-117 VAT amount | cbc:TaxAmount | ram:CalculatedAmount |
| BT-118 VAT category | cac:TaxCategory/cbc:ID | ram:CategoryCode |
| BT-119 VAT rate | cac:TaxCategory/cbc:Percent | ram:RateApplicablePercent |
| BT-120 / BT-121 Exemption reason / code | cac:TaxCategory/cbc:TaxExemptionReason, cbc:TaxExemptionReasonCode | ram:ExemptionReason, ram:ExemptionReasonCode |
| BG-22 Document totals | cac:LegalMonetaryTotal | Settlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation |
| BT-106 Sum of line net amounts | cbc:LineExtensionAmount | ram:LineTotalAmount |
| BT-107 / BT-108 Sum of allowances / charges | cbc:AllowanceTotalAmount, cbc:ChargeTotalAmount | ram:AllowanceTotalAmount, ram:ChargeTotalAmount |
| BT-109 Total without VAT | cbc:TaxExclusiveAmount | ram:TaxBasisTotalAmount |
| BT-110 Total VAT | cac:TaxTotal/cbc:TaxAmount | ram:TaxTotalAmount with currencyID |
| BT-112 Total with VAT | cbc:TaxInclusiveAmount | ram:GrandTotalAmount |
| BT-113 Paid amount | cbc:PrepaidAmount | ram:TotalPrepaidAmount |
| BT-114 Rounding amount | cbc:PayableRoundingAmount | ram:RoundingAmount |
| BT-115 Amount due | cbc:PayableAmount | ram:DuePayableAmount |
The VAT total is where the structures diverge most. UBL puts BT-110 in cac:TaxTotal next to the breakdown and outside LegalMonetaryTotal; CII puts it inside the monetary summation with a currency attribute. When the VAT accounting currency (BT-6) differs from the invoice currency, UBL adds a second cac:TaxTotal and CII a second ram:TaxTotalAmount in that currency (BT-111). The arithmetic rules that tie these totals together, such as BR-CO-15, are identical in both syntaxes.
Invoice lines
A line is cac:InvoiceLine in a UBL invoice, cac:CreditNoteLine in a credit note, and ram:IncludedSupplyChainTradeLineItem in CII for both. CII splits each line into the same agreement, delivery and settlement branches as the header:
| Business term | UBL, under the line | CII, under the line item |
| BT-126 Line identifier | cbc:ID | ram:AssociatedDocumentLineDocument/ram:LineID |
| BT-127 Line note | cbc:Note | ram:AssociatedDocumentLineDocument/ram:IncludedNote/ram:Content |
| BT-129 Quantity | cbc:InvoicedQuantity or cbc:CreditedQuantity | ram:SpecifiedLineTradeDelivery/ram:BilledQuantity |
| BT-130 Unit of measure | unitCode attribute of the quantity | unitCode attribute of the quantity |
| BT-131 Line net amount | cbc:LineExtensionAmount | ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeSettlementLineMonetarySummation/ram:LineTotalAmount |
| BT-146 Item net price | cac:Price/cbc:PriceAmount | ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount |
| BT-148 Item gross price | cac:Price/cac:AllowanceCharge/cbc:BaseAmount | ram:SpecifiedLineTradeAgreement/ram:GrossPriceProductTradePrice/ram:ChargeAmount |
| BT-149 Price base quantity | cac:Price/cbc:BaseQuantity | ram:NetPriceProductTradePrice/ram:BasisQuantity |
| BG-27 / BG-28 Line allowance / charge | cac:AllowanceCharge | ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeAllowanceCharge |
| BT-151 Line VAT category | cac:Item/cac:ClassifiedTaxCategory/cbc:ID | ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:CategoryCode |
| BT-152 Line VAT rate | cac:Item/cac:ClassifiedTaxCategory/cbc:Percent | ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent |
| BT-153 Item name | cac:Item/cbc:Name | ram:SpecifiedTradeProduct/ram:Name |
| BT-154 Item description | cac:Item/cbc:Description | ram:SpecifiedTradeProduct/ram:Description |
| BT-155 Seller's item identifier | cac:Item/cac:SellersItemIdentification/cbc:ID | ram:SpecifiedTradeProduct/ram:SellerAssignedID |
| BT-157 Item standard identifier (GTIN) | cac:Item/cac:StandardItemIdentification/cbc:ID with schemeID | ram:SpecifiedTradeProduct/ram:GlobalID with schemeID |
Note where the line VAT lives: on the item in UBL, on the line settlement in CII. The price is in the line agreement in CII but a sibling of the item in UBL. Mapping code that walks the two trees in parallel tends to put one of them in the wrong branch.
Where the two syntaxes really differ
Most of the mapping is renaming. These are the places where it is not, and where hand-written XSLT and mapping code usually go wrong:
- One root or two. UBL has separate
Invoice and CreditNote documents with different element names (InvoiceLine and CreditNoteLine, InvoicedQuantity and CreditedQuantity). CII has one root and the type code. A UBL to CII mapping reads both documents; a CII to UBL mapping has to choose the UBL document from BT-3.
- Dates. ISO dates in UBL,
udt:DateTimeString with format 102 (YYYYMMDD) in CII.
- Tax identifiers. TaxScheme ID
VAT in UBL, schemeID VA or FC in CII.
- Names. The legal name moves between the party and its legal entity, and the trading name the other way.
- VAT total and second currency. Outside the monetary total in UBL, inside it in CII, with an extra element for the VAT accounting currency.
- The due date on credit notes. A UBL credit note has no
cbc:DueDate; BT-9 goes to the payment means. CII keeps it in the payment terms for both.
- Element order. Both schemas define a fixed sequence. A value in the right element but the wrong position fails schema validation before a single business rule runs.
- BT-24 and BT-23. The identifiers declare the rule set and process. They have to match the target ecosystem, not the source: a CII written into a Factur-X declares a Factur-X profile, a UBL sent over Peppol declares Peppol BIS.
Code lists, by contrast, are shared. VAT categories, payment means codes, units of measure, allowance and charge reason codes and country codes take the same values on both sides, so a mapping never translates codes, only moves them.
Mapping once instead of twice
The cleanest way to support both syntaxes is not to map between them at all, but to map your own data once to the semantic model and let the model produce either syntax. That is how the InvoiceXML create endpoints work: one JSON document model, named after the business terms (invoiceNumber, seller.vatIdentifier, lines[].priceDetails.netPrice), and one endpoint per output. The same JSON posted to /v1/create/ubl returns UBL and posted to /v1/create/cii returns CII, each validated against its rules, and posted to /v1/create/facturx or /v1/create/zugferd it returns the hybrid PDF with the CII inside.
The model also works in the other direction. /v1/extract/json reads a UBL file, a CII file or the XML inside a Factur-X or ZUGFeRD into that same JSON, so an incoming invoice in either syntax lands in one shape in your system. The UBL and CII API toolkits show both directions in code.
Converting without a mapping of your own
When the invoice already exists as XML in one syntax, a conversion endpoint does the whole mapping above in one call, validates the result, and answers with findings instead of a broken file when the source cannot produce a valid one:
curl -X POST https://api.invoicexml.com/v1/convert/ubl/to/cii \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "[email protected]" \
--output invoice-cii.xml
curl -X POST https://api.invoicexml.com/v1/convert/cii/to/ubl \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "[email protected]" \
-F "profile=peppol-bis-3" \
--output invoice-peppol.xml
The mapping tables stay useful even then: when a receiver questions a value, this page tells you where to find it in either file. To check a file you produced yourself, /v1/validate/ubl and /v1/validate/cii run the full rule set and name each finding with its rule and business term.
Get started
Take one invoice you already have in one syntax, convert it to the other, and compare the two files against the tables above: it is the fastest way to learn both shapes. Create a free InvoiceXML account → and get 100 credits for free, no credit card required.
Related resources:
InvoiceXML is a REST API for European e-invoice compliance covering ZUGFeRD, Factur-X, XRechnung, Peppol UBL, and CII. Stateless processing, GDPR compliant by architecture, and callable from any stack: C#, Node.js, Python, PHP, Java, Go, or anything else with an HTTP client.