UBL Validation API
Validate UBL 2.1 invoices and credit notes against the UBL XSD schema, EN 16931 Schematron, and the right Peppol BIS Billing 3.0 or national CIUS overlay, returned as structured JSON in under two seconds.
UBL is the native XML syntax of the Peppol network and one of the two EN 16931 syntaxes. The API auto-detects the applied CIUS from the CustomizationID (BT-24), Peppol BIS Billing 3.0, NLCIUS, EHF Billing 3.0, XRechnung in UBL, or PINT, and runs the same multi-layer checks a Peppol access point applies before accepting a document for delivery. Every violation is reported with rule ID, severity, and XPath location.
curl -X POST https://api.invoicexml.com/v1/validate/ubl \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "[email protected]"
{ "valid": true, "data": { "profile": "ehf", "customizationId": "urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0#conformant#urn:fdc:anskaffelser.no:2019:ehf:spec:3.0" }, "errors": [], "warnings": [] }
Try it live, no API key required
This runs the same POST /v1/validate/ubl endpoint your integration will call. Drop any UBL XML, Peppol BIS, NLCIUS, EHF, XRechnung, or PINT, to see the full validation response and detected profile.
Drop your UBL XML here
or browse files to upload
POST /v1/validate/ubl · Accepted: UBL 2.1 Invoice or CreditNote XML · Max 20 MB
One UBL syntax, many country profiles. The CustomizationID picks the rules.
UBL validation is layered. Every invoice is first checked against the OASIS UBL 2.1 XSD, then the 200+ EN 16931 business rules, then a Customised Usage Specification (CIUS) that narrows EN 16931 for a specific network or country. The CIUS is never something you pass as a parameter: the API reads it from the CustomizationID (BT-24) inside the document and applies the matching overlay automatically. A document declaring the Peppol BIS Billing 3.0 identifier gets the Peppol overlay; a Norwegian EHF document declaring the anskaffelser.no conformant identifier gets the EHF overlay on top of Peppol.
Supported overlays, all from one endpoint:
- Peppol BIS Billing 3.0, the cross-border baseline for the whole Peppol network.
- NLCIUS (Netherlands, SimplerInvoicing), the Dutch CIUS used for domestic and public-sector invoicing.
- EHF Billing 3.0 (Norway), built on Peppol BIS and mandatory for the Norwegian public sector.
- XRechnung in UBL syntax (Germany), the German CIUS for B2G, also expressible in the CII syntax.
- PINT and A-NZ PINT (Peppol International), the basis for Australia, New Zealand, Singapore, Japan, and Malaysia.
Because detection is driven by the document, you can onboard a Belgian supplier (B2B mandate from 2026), a Dutch buyer on NLCIUS, and a Norwegian counterparty on EHF without writing a single branch in your integration. The same /v1/validate/ubl call returns the profile it actually applied in data.profile.
Not just pass or fail. A full UBL compliance report.
A Peppol access point rejection tells you a document failed and little else. The InvoiceXML UBL API returns a complete report array on every response: one row for each EN 16931 Business Term found in your Invoice or CreditNote, each with the extracted value and a pass or fail verdict, evaluated against the CIUS the document actually declared. Run it as a pre-submission pre-flight and catch Peppol BIS, EHF, or NLCIUS problems before the document reaches the network, with zero cac: and cbc: XML parsing on your side.
UBL Compliance Report
invoice-ubl.xml · Peppol BIS Billing 3.0 · EHF Billing 3.0 (Norway)
| Status | Business Term | Field | Value |
|---|---|---|---|
| BT-1 Invoice number | invoiceNumber | EHF-2026-04417 | |
| BT-5 Invoice currency | currency | NOK | |
| BT-27 Seller name | seller.name | Nordisk Handel AS | |
| BT-31 Seller VAT identifier | seller.vatIdentifier | NO999888777MVA | |
| BT-49 Buyer electronic address | buyer.electronicAddress | Missing. Peppol BIS requires a buyer electronic address. | |
| BT-112 Invoice total with VAT | totals.grandTotalAmount | 14 875.00 | |
| BT-115 Amount due for payment | totals.duePayableAmount | Inconsistent. Document total cannot be reconciled. |
Rendered from the report array. Each row is one object: code, name, section, path, value, isValid, and error. Example supplier Nordisk Handel AS (Norway), declared profile EHF Billing 3.0 over Peppol BIS, currency NOK.
Peppol access point pre-flight
Run the report before you hand a document to your access point. A single failing row exposes the exact Business Term that would trigger a Peppol BIS, EHF, or NLCIUS rejection, so you fix it in your own UI instead of decoding a cryptic network rejection code hours later. The same pre-flight protects you for the Belgium B2B 2026 mandate, the Netherlands, and the Nordics from one integration.
Inline form highlighting
The path on every report row, and the fields array on every friendly error, map straight to your invoice form inputs. Mark the failing field red and show the message beside it, so a Norwegian supplier corrects the exact value, for example buyer.electronicAddress or seller.vatIdentifier, before resubmitting an EHF invoice.
Multi-country profile coverage
The report is built the same way whether the document is a plain EN 16931 UBL invoice, a Peppol BIS 3.0 invoice from any market, an NLCIUS invoice for a Dutch buyer, an EHF invoice for Norway, an XRechnung in UBL for Germany, or a PINT invoice for Australia, New Zealand, Singapore, Japan, or Malaysia. It covers both Invoice and CreditNote types, so one rendering component handles every profile and trading partner you onboard.
Skip the API. Use getubl.com
Our online companion tool. Create and validate UBL invoices in your browser, no integration, no API key, no code. Perfect for freelancers, small businesses, and one-off invoices.
Validate UBL REST API Request
A single endpoint handles every UBL invoice and credit note profile. Document type and CIUS are detected from the CustomizationID inside the submitted XML automatically:
$ curl -X POST https://api.invoicexml.com/v1/validate/ubl \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "[email protected]" # Also accepts UBL CreditNote documents $ curl -X POST https://api.invoicexml.com/v1/validate/ubl \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "[email protected]"
You only send the file. The API reads the CustomizationID, detects the document type and CIUS, and returns the detected values. No profile parameter to set or keep in sync.
Validate UBL API Response
Both valid and invalid documents return HTTP 200. The data.profile field tells you which rule set the API validated against, as a stable slug like peppol-bis-3, nlcius, ehf, xrechnung or pint, critical when you are processing invoices from trading partners across the Netherlands, Norway, Germany, and the wider Peppol network who declare different profiles in their CustomizationID. The full declared identifier is echoed in data.customizationId.
The layer field on each finding distinguishes structural failures (xsd) from EN 16931 base rule violations (en16931) and profile overlay violations (cius). A cius-layer error on an EHF or Peppol BIS invoice means your document satisfies the European standard but fails the network or national overlay, it would be rejected by your access point even though it is technically EN 16931 compliant.
Each friendly error also carries btCodes and a fields array of dotted JSON paths the error maps to, so a front-end form can highlight the exact input that failed, for example buyer.electronicAddress. Every response, valid or not, also includes a report array with one row per EN 16931 Business Term found on the document, each with its value and a pass or fail flag, ready to render as a compliance table.
Valid UBL Response (EHF, Norway)
// 200 OK { "valid": true, "detail": "Your invoice is UBL 2.1 compliant and meets the EHF Billing 3.0 (Norway) rules over Peppol BIS Billing 3.0.", "data": { "schemaValid": true, "schematronValid": true, "conformanceLevel": "UBL 2.1", "profile": "ehf", "customizationId": "urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0#conformant#urn:fdc:anskaffelser.no:2019:ehf:spec:3.0" }, "errors": [], "warnings": [], "report": [ { "code": "BT-1", "name": "Invoice number", "section": "header", "path": "invoiceNumber", "value": "EHF-2026-04417", "isValid": true, "errors": [], "warnings": [] } // one row per EN 16931 business term, see the compliance report section below ] }
Invalid UBL Response (Peppol BIS overlay)
// 200 OK { "valid": false, "detail": "Validation failed with 3 error(s)", "data": { "schemaValid": true, "schematronValid": false, "conformanceLevel": "UBL 2.1", "profile": "ehf", "customizationId": "urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0#conformant#urn:fdc:anskaffelser.no:2019:ehf:spec:3.0" }, "errors": [ { "rule": "PEPPOL-EN16931-R010", "layer": "cius", "line": null, "message": "Peppol BIS requires a buyer electronic address. Add the buyer's electronic address (a buyer VAT identifier is used as a fallback), or use the plain en16931 profile if the invoice is not meant for Peppol.", "btCodes": ["BT-49"], "fields": ["buyer.electronicAddress"], "raw": "[PEPPOL-EN16931-R010] CIUS: Buyer electronic address MUST be provided (at /*:Invoice/cac:AccountingCustomerParty)" }, { "rule": "BR-CO-14", "layer": "en16931", "line": null, "message": "The invoice total VAT amount does not match the sum of VAT breakdown amounts. Check that all VAT calculations are consistent.", "btCodes": ["BT-110"], "fields": ["totals.taxTotalAmount"], "raw": "[BR-CO-14] Invoice total VAT amount must equal the sum of VAT breakdown amounts. (at /*:Invoice/cac:TaxTotal)" }, { "rule": "BR-61", "layer": "en16931", "line": null, "message": "Payment means code 30 or 58 requires a PaymentMeans/PayeeFinancialAccount/ID (IBAN). Add the payee account identifier to the payment means block.", "btCodes": ["BT-84"], "fields": ["paymentDetails.paymentAccountIdentifier"], "raw": "[BR-61] If the payment means code is 30 or 58, a payment account identifier must be present. (at /*:Invoice/cac:PaymentMeans)" } ], "warnings": [], "report": [ // full field-by-field report, see the compliance report section below { "code": "BT-49", "name": "Buyer electronic address", "section": "buyer", "path": "buyer.electronicAddress", "value": null, "isValid": false, "errors": ["Peppol BIS requires a buyer electronic address."], "warnings": [] } ] }
Unknown CIUS? You still get a verdict
If the CustomizationID declares a profile the API has no dedicated rules for, the document is validated against the EN 16931 base rules instead of being rejected, and the response says exactly what happened:
// 200 OK { "valid": true, "detail": "Your invoice is UBL 2.1 compliant and meets the EN 16931 specifications.", "data": { "schemaValid": true, "schematronValid": true, "conformanceLevel": "UBL 2.1", "profile": null, "customizationId": "urn:cen.eu:en16931:2017#compliant#urn:example:custom-cius:v9" }, "errors": [], "warnings": [ { "rule": "PROFILE-DETECTION", "layer": "cius", "line": null, "message": "The declared CustomizationID 'urn:cen.eu:en16931:2017#compliant#urn:example:custom-cius:v9' is not a profile this validator has dedicated rules for. The invoice was validated against the EN 16931 base rules only.", "btCodes": [], "fields": [], "raw": "[PROFILE-DETECTION] CIUS: The declared CustomizationID 'urn:cen.eu:en16931:2017#compliant#urn:example:custom-cius:v9' is not a profile this validator has dedicated rules for. The invoice was validated against the EN 16931 base rules only." } ] }
OpenPeppol ships in spring and autumn waves. We switch on the go-live date, you change nothing.
OpenPeppol publishes Peppol BIS Billing 3.0 updates in scheduled spring and autumn release waves, each with a fixed go-live date, and the national CIUS bodies (NLCIUS, EHF, XRechnung, PINT) update on their own cadence. Self-hosted open-source validators such as the KoSIT validator or Mustang put that whole calendar on you: you have to track every release wave, download the new Schematron and XSD, upgrade the library, redeploy, and hope nothing regressed before the rules go live.
InvoiceXML removes that work entirely. When OpenPeppol or a national authority publishes a new XSD schema, Schematron rule set, or validation artifact, we ship the updated artifacts and switch /v1/validate/ubl to them on the standard's official effective date, automatically. Your integration keeps calling the same endpoint and is always validating against the currently-effective rules: no tracking release waves, no monitoring standards bodies, no library upgrades, no redeploys, no maintenance.
Zero changes on your side
Same endpoint, same request. The currently-effective rule set is applied for you on each release date.
No silent drift
Eliminates the risk of an integration falling out of compliance the day a Peppol wave goes live.
0.3 to 0.5 FTE removed
The compliance-maintenance burden a self-hosted validator demands is no longer yours to carry.
What the UBL validation API checks
UBL 2.1 XSD Schema
Validates the document against the OASIS UBL 2.1 XSD schema, UBL-Invoice-2.1.xsd for Invoice documents and UBL-CreditNote-2.1.xsd for CreditNote documents. Catches structural errors in the cac: and cbc: component hierarchy, incorrect data types in monetary amounts and quantities, missing required elements, and namespace declaration errors. The document type is detected from the root element before the correct schema is applied.
EN 16931 Schematron
Validates all 200+ EN 16931 business rules against the UBL syntax. Includes the UBL-specific rule implementations: cac:TaxTotal/cbc:TaxAmount arithmetic (BR-CO-14), PaymentMeans account identifier rules (BR-61, BR-62), party address requirements (BR-07, BR-08), and VAT category conditional rules (BR-AE-05 through BR-E-02). Errors from this layer indicate European standard non-compliance, the common ground under Peppol BIS, EHF, NLCIUS, XRechnung, and PINT.
Peppol BIS and National CIUS Rules
When the CustomizationID declares a Peppol or national CIUS, the API automatically runs the corresponding overlay Schematron on top of EN 16931. Peppol BIS Billing 3.0 rules (PEPPOL-EN16931-Rxx codes) enforce network-specific constraints like invoice type code values and endpoint identifier schemes. National overlays for NLCIUS (Netherlands), EHF Billing 3.0 (Norway), XRechnung (Germany), and PINT (Australia, New Zealand, Singapore, Japan, Malaysia) add their own requirements. Errors from this layer cause Peppol access point rejection even when EN 16931 passes.
CustomizationID Profile Detection
Reads the cbc:CustomizationID element (BT-24) to identify the declared CIUS and routes the document to the correct validation stack. A plain EN 16931 UBL invoice, a Peppol BIS 3.0 invoice, an NLCIUS invoice for a Dutch buyer, an EHF invoice for Norway, and a PINT invoice for Australia or New Zealand all declare different CustomizationID values and require different Schematron artefacts. The API handles all of them from a single endpoint, no profile parameter required.
UBL Validation Response Schema
Both valid and invalid documents return HTTP 200. The profile and customizationId fields confirm exactly which CIUS was validated, useful when processing invoices from trading partners across Belgium, the Netherlands, Norway, Germany, and the wider Peppol markets who may declare different profiles. The layer field on each error tells you whether to fix an EN 16931 data problem or a Peppol network-specific configuration issue. Create/convert endpoints return HTTP 400 with the same errors structure on validation failure.
| Field | Type | Description |
|---|---|---|
| Always present | ||
| valid | boolean | Primary flag to branch on. true when the document passes UBL XSD, EN 16931 Schematron, and all applicable CIUS overlay rules. |
| detail | string | Human-readable summary naming the rule set that was applied, e.g. "Your invoice is UBL 2.1 compliant and meets the EHF Billing 3.0 (Norway) rules." |
| data.conformanceLevel | string | Syntax conformance label, e.g. UBL 2.1. |
| data.profile | string? | The profile whose rules were applied, as a stable slug: peppol-bis-3, en16931, nlcius, ehf, xrechnung or pint. Null when the declared CustomizationID has no dedicated rule set (the EN 16931 base rules are applied and a PROFILE-DETECTION warning explains it). |
| data.customizationId | string? | Full CustomizationID URI (BT-24) read from the submitted XML, e.g. urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0. |
| data.schemaValid | boolean | UBL 2.1 XSD schema check passed for the detected document type. |
| data.schematronValid | boolean | All EN 16931 and applicable CIUS Schematron rules passed. |
| Findings (always present) | ||
| errors | object[] | Flat array of finding objects, one per rule violation. Empty array when the document is valid. |
| warnings | object[] | Flat array of finding objects for non-fatal issues. The document is still compliant when only warnings are present. May contain entries on a valid response. |
| [].rule | string | Rule identifier, e.g. PEPPOL-EN16931-R010, BR-CO-14, BR-61. |
| [].layer | string | Validation layer that produced the finding: xsd (structure), en16931 (European standard base rules), or cius (the profile overlay: Peppol BIS, EHF, NLCIUS, XRechnung or PINT rules). |
| [].line | int? | Invoice line item number from the InvoiceLine sequence, or null for document-level findings. |
| [].message | string | Friendly, human-readable description referencing the UBL element concept in plain language, not the cac:/cbc: XPath. |
| [].btCodes | string[] | EN 16931 Business Term codes the rule references, e.g. ["BT-49"]. |
| [].fields | string[] | Dotted JSON paths the finding maps to, e.g. ["buyer.electronicAddress"]. Use them to highlight the exact form input that failed. |
| [].raw | string | Verbatim technical validator output, including the rule code and XPath location, e.g. "[PEPPOL-EN16931-R010] Buyer electronic address MUST be provided. (at /*:Invoice/cac:AccountingCustomerParty)". |
| Compliance report (always present) | ||
| report | object[] | One row per EN 16931 Business Term found on the invoice, with its value and a pass/fail flag. null if the report could not be built. |
| report[].code | string | Business Term code, e.g. BT-1. |
| report[].name | string | Human-readable term name, e.g. Invoice number. |
| report[].section | string | Grouping for display: header, seller, buyer, lines, payment, totals, or tax. |
| report[].path | string | Dotted JSON path to the field, e.g. lines[0].priceDetails.netPrice. |
| report[].value | string? | Current value of the term, or null when absent. |
| report[].isValid | boolean | false when a validation error references this term, otherwise true. |
| report[].errors | string[] | All friendly error messages on this Business Term. Empty array when the term has no errors. |
| report[].warnings | string[] | All friendly warning messages on this Business Term. Empty array when the term has no warnings. |
Integrate UBL validation into your workflow
REST API
Call POST /v1/validate/ubl from any language. Invoice and CreditNote documents handled automatically. Returns structured JSON in under 2 seconds. Use the profile field to log which CIUS your Peppol trading partners are submitting: NLCIUS, EHF, XRechnung, PINT, or plain Peppol BIS. A profile mismatch between sender and receiver is a frequent source of silent Peppol delivery failures.
API docsZapier / Make / n8n
Validate UBL invoices received from your Peppol access point before they enter your AP system. Branch on the layer field in the friendly errors array: a cius error means a Peppol BIS or national overlay issue the supplier needs to fix, while an en16931 error means a data problem in the invoice itself. Route each type to a different notification workflow as you onboard suppliers across the Netherlands, the Nordics, and Belgium's 2026 B2B mandate.
Automation integrationsCI/CD Pipeline
OpenPeppol ships Peppol BIS updates in spring and autumn release waves. When a new wave goes live, the InvoiceXML rules switch automatically on the effective date, so your pipeline catches PEPPOL-EN16931-Rxx regressions before non-compliant invoices reach your access point and generate network rejection codes with no obvious link to the change that caused them. No Schematron download, no library bump, no redeploy on your side.
View cURL exampleComplete UBL Toolkit
Everything you need to create, convert, validate, and preview UBL invoices, via REST API or online.
Frequently Asked Questions
Which Peppol and national UBL profiles does the validation API support?
The API reads the CustomizationID (BT-24) and applies the matching CIUS Schematron automatically. Supported profiles are plain EN 16931 UBL, Peppol BIS Billing 3.0, NLCIUS (Netherlands, SimplerInvoicing), EHF Billing 3.0 (Norway, built on the Peppol rule set), XRechnung in UBL syntax (Germany), and PINT including A-NZ PINT (Peppol International, covering Australia, New Zealand, Singapore, Japan, and Malaysia). Both Invoice and CreditNote document types are supported for every profile.
Who keeps the UBL and Peppol BIS rules up to date, and when do new versions take effect?
InvoiceXML does. OpenPeppol publishes Peppol BIS Billing 3.0 updates in scheduled spring and autumn release waves, each with a fixed go-live date, and national authorities update their CIUS (NLCIUS, EHF, XRechnung, PINT) on their own cadence. When a new XSD, Schematron rule set, or validation artifact is published, we ship it and switch /v1/validate/ubl to it on the standard's official effective date. Your integration keeps calling the same endpoint and is always validating against the currently-effective rules, with no tracking of release waves, no library upgrades, and no redeploys on your side. That removes the 0.3 to 0.5 FTE compliance-maintenance burden a self-hosted validator (KoSIT validator, Mustang) puts on your team and the risk of silently falling out of compliance the day a wave goes live.
Does the API validate UBL CreditNote documents as well as invoices?
Yes. UBL defines Invoice and CreditNote as separate document types with slightly different element constraints. The API detects the root element automatically, Invoice or CreditNote, and applies the correct XSD schema and Schematron rules for each, including the matching Peppol BIS or national CIUS overlay. CreditNote-specific rules around negative amounts, reason codes, and document references are validated correctly.
Why does my UBL invoice pass EN 16931 validation but get rejected by the Peppol network?
EN 16931 and Peppol BIS Billing 3.0 are two different validation layers. A document can satisfy all 200+ EN 16931 business rules and still fail the Peppol BIS overlay, for example PEPPOL-EN16931-R010 (a buyer electronic address must be provided) or PEPPOL-EN16931-R004 (the CustomizationID must be exactly the Peppol BIS identifier). The InvoiceXML UBL validation API runs both layers automatically when the CustomizationID declares a Peppol or national profile, so pre-submission validation catches access-point rejections before they reach the network. The layer field on each finding tells you whether the failure came from the en16931 base rules or the cius overlay.
Ready to automate your invoices?
Validate, convert and embed compliant e-invoices through one API. Start your 30-day free trial. No credit card required.