Peppol BIS Billing 3.0
Generate and validate UBL invoices ready for the Peppol Network with Beliq.
Peppol BIS Billing 3.0 is the Core Invoice Usage Specification (CIUS) of EN 16931 used by the Peppol Network to exchange invoices and credit notes between businesses and public authorities across Europe.
It is the format you need when:
- Your buyer requires an invoice on Peppol (Belgium B2B, the Netherlands, the Nordics, and a growing number of EU public-sector procurement portals)
- You sell cross-border in the EU and want a single UBL format that is accepted in most member states
- Your ERP or Access Point speaks UBL and uses the Peppol BIS 3.0 customization identifier
To generate Peppol-compliant XML with Beliq, set:
{
"standard": "peppol-bis",
"profile": "peppol",
"output": "xml"
}profile is optional — if omitted, Beliq picks peppol automatically when standard: "peppol-bis".
What Beliq emits
When you request standard: "peppol-bis", Beliq generates a UBL 2.1 invoice with the elements that Peppol requires beyond plain EN 16931:
| Element | Value | Source |
|---|---|---|
cbc:CustomizationID |
urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0 |
Fixed by Peppol BIS Billing 3.0 |
cbc:ProfileID |
urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 (Billing 3.0 default) |
Beliq default; can be overridden |
cbc:BuyerReference or cac:OrderReference |
Your buyerReference / orderReference field |
Required by PEPPOL-EN16931-R003 |
cac:AccountingSupplierParty → cbc:EndpointID |
Seller Peppol electronic-address with @schemeID |
Required by PEPPOL-EN16931-R020 |
cac:AccountingCustomerParty → cbc:EndpointID |
Buyer Peppol electronic-address with @schemeID |
Required by PEPPOL-EN16931-R010 |
Everything else (party names, addresses, VAT IDs, invoice lines, tax breakdown, totals) follows the standard UBL 2.1 / EN 16931 layout.
Peppol electronic-address
Every party on the Peppol Network has an electronic-address — a pair of values that uniquely identifies them on the network. You provide it on each party as:
"peppol": {
"schemeId": "9930",
"id": "DE123456789"
}schemeId comes from the official Peppol EAS code list. The most common entries are:
Countries with vatId fallback
For these 11 countries, providing vatId and address.countryCode is enough — Beliq picks the canonical EAS scheme automatically, because the VAT number is itself a valid identifier under that country’s scheme:
| Country | schemeId |
Scheme name | What id is |
|---|---|---|---|
| Austria | 9914 |
AT:VAT | VAT number (ATU…) |
| Belgium | 9925 |
BE:VAT | VAT number (BE…) |
| France | 9957 |
FR:VAT | VAT number (FR…) |
| Germany | 9930 |
DE:VAT | VAT number (DE…) |
| Ireland | 9935 |
IE:VAT | VAT number (IE…) |
| Italy | 0211 |
IT:PARTITA IVA | VAT number (IT…) |
| Luxembourg | 9938 |
LU:VAT | VAT number (LU…) |
| Netherlands | 9944 |
NL:VAT | VAT number (NL…B01) |
| Poland | 9945 |
PL:VAT | VAT number (PL…) |
| Portugal | 9946 |
PT:VAT | VAT number (PT…) |
| Spain | 9920 |
ES:VAT | VAT number (ES…) |
Countries that require an explicit peppol block
For these countries the VAT number is not a valid identifier under the country’s Peppol scheme. Beliq does not silently transform values, so you must supply both peppol.schemeId and peppol.id for parties registered in:
| Country | schemeId |
Scheme name | What id is |
Example |
|---|---|---|---|---|
| Denmark | 0184 |
DK:CVR | CVR number, 8 digits, without the DK prefix your VAT id carries |
12345678 |
| Finland | 0216 |
FI:OVT2 | OVT identifier: 0037 + the 8-digit business id |
003712345678 |
| Norway | 0192 |
NO:ORG.NR | Enhetsregisteret organisasjonsnummer (9 digits) | 123456789 |
| Sweden | 0007 |
SE:ORG | Organisationsnummer (10 digits) | 5567123456 |
"buyer": {
"name": "Eksempel AS",
"vatId": "NO123456789MVA",
"peppol": { "schemeId": "0192", "id": "123456789" },
"address": { "city": "Oslo", "postalCode": "0150", "countryCode": "NO" }
}If a recipient in one of these countries is supplied with vatId only, the engine fails closed with a clear error — silently stripping the MVA suffix or the DK prefix, or pivoting to a different scheme, would risk producing an endpoint that validates but cannot be reached.
Denmark is the one to watch: a Danish endpoint spelled DK12345678 passes Peppol validation (PEPPOL-COMMON-R042 accepts both spellings) and then fails to resolve, because Danish participants are registered under the bare CVR.
Finland’s old 0213 scheme was removed from the Peppol network on 2024-12-31. Beliq rejects it, and any other removed scheme, at build time.
Other countries
For countries outside both tables (or for any party identified by a non-VAT scheme such as GLN 0088 or Leitweg-ID 0204), set peppol.schemeId and peppol.id explicitly using a value from the official Peppol EAS code list. The full snapshot of the codelist Beliq accepts is pinned in the engine and unknown codes are rejected at build time.
What Beliq validates
Every invoice you generate is validated against three layered Schematron rule sets, in this order:
- XSD — UBL 2.1 structural validity
- EN 16931 — common European business rules (BR-01, BR-CO-, BR-S-, etc.)
- OpenPeppol BIS Billing 3.0 Schematron — Peppol-specific cross-border rules (
PEPPOL-EN16931-R*) and country-specific rules (PEPPOL-COMMON-R*, national*-R-*)
If any rule fires with severity error or fatal, the response is 422 INVALID_INVOICE with the failing rule IDs in validationResult.errors. The most common Peppol rules to watch for:
| Rule | Trigger |
|---|---|
PEPPOL-EN16931-R003 |
Missing both buyerReference and orderReference |
PEPPOL-EN16931-R010 |
Buyer party has no Peppol EndpointID |
PEPPOL-EN16931-R020 |
Seller party has no Peppol EndpointID |
PEPPOL-EN16931-R030 |
BuyerReference or OrderReference.ID is empty |
BR-CO-15 |
Sum of line/tax/total amounts is inconsistent |
The exact Schematron version Beliq runs is published on the validation artifacts page.
Schematron 3.0.21 is in force: OpenPEPPOL made it mandatory on the network on 2026-08-17, so it took effect without a notice period and latest resolves to it now. 3.0.20 stays selectable on the previous channel until 2027-03-03, which is how you reproduce an earlier verdict. Note that a document 3.0.20 passes may still be rejected by the receiving access point, since the mandate is the network’s, not ours.
When validation passes, the /v1/validate response includes:
{
"valid": true,
"format": "ubl",
"profileDetected": "peppol",
"schematronVersion": "1.3.16",
"ciusVersion": "PeppolBIS-Billing-3.0.21",
"peppolVersion": "PeppolBIS-Billing-3.0.21",
"errors": [],
"warnings": []
}Getting the document onto the network
Beliq generates and validates Peppol-compliant XML. Today it does not put that document on the Peppol Network: to reach a buyer’s Access Point, hand the generated XML to your own Access Point provider. What a Peppol Access Point is covers how the network routes a document and how to pick a provider; OpenPeppol publishes the certified list.
Managed delivery over Peppol is in build, targeting Q4 2026 and subject to certification. Either way the correctness layer is the same, and your choice of Access Point is never dictated by us.
Credit notes
Peppol BIS Billing 3.0 emits CreditNote documents using the UBL CreditNote root element with cbc:CreditNoteTypeCode = 381. Beliq also supports CreditNote round-trip on XRechnung (UBL and CII), ZUGFeRD 2.x, Factur-X 1.09.2 and Slovenia’s e-SLOG 2.0 (document type code 381 on the same e-SLOG message shape): see the format overview and DOCUMENT_TYPE_STANDARD_MISMATCH. Italy FatturaPA and Spain Facturae accept invoices only; setting documentType: "creditnote" on either returns a 422 with a clear error.
To emit a CreditNote, set documentType: "creditnote" on the invoice payload and provide a precedingInvoiceReference linking back to the original invoice:
{
"standard": "peppol-bis",
"profile": "peppol",
"output": "xml",
"invoice": {
"documentType": "creditnote",
"precedingInvoiceReference": {
"id": "INV-2026-001",
"issueDate": "2026-04-12"
},
"number": "CN-2026-001",
"issueDate": "2026-04-25",
"currencyCode": "EUR",
"buyerReference": "NL-PO-7711",
"seller": {
"name": "Test Verkoper B.V.",
"vatId": "NL123456789B01",
"peppol": { "schemeId": "9944", "id": "NL123456789B01" },
"address": { "street": "Damrak 1", "city": "Amsterdam", "postalCode": "1012 LG", "countryCode": "NL" }
},
"buyer": {
"name": "Test Koper B.V.",
"vatId": "NL987654321B02",
"peppol": { "schemeId": "9944", "id": "NL987654321B02" },
"address": { "street": "Coolsingel 2", "city": "Rotterdam", "postalCode": "3011 AA", "countryCode": "NL" }
},
"lines": [
{
"description": "Credit for returned licences",
"quantity": 1,
"unitCode": "C62",
"unitPrice": 100.00,
"lineTotal": 100.00,
"vatRate": 21,
"vatCategoryCode": "S"
}
],
"taxSummary": [
{ "vatCategoryCode": "S", "vatRate": 21, "taxableAmount": 100.00, "taxAmount": 21.00 }
],
"totalNetAmount": 100.00,
"totalTaxAmount": 21.00,
"totalGrossAmount": 121.00
}
}precedingInvoiceReference.id becomes the UBL cac:BillingReference / cac:InvoiceDocumentReference / cbc:ID element. issueDate is optional but recommended — when present it is emitted as cbc:IssueDate on the same reference block.
Validation runs the same three layers as for an Invoice (XSD → EN 16931 → Peppol BIS Schematron). The relevant Peppol rule for a Peppol CreditNote is PEPPOL-EN16931-P0101 — when the document’s root element is CreditNote, the Peppol-declared invoice type must stay 381. Use documentType plus /v1/generate; the same discriminator applies on the other CreditNote-capable generators (xrechnung, facturx, zugferd, eslog) via the route CREDITNOTE_CAPABLE_STANDARDS — see format overview.
Common patterns
B2G in Belgium
For invoices to Belgian public-sector buyers (mandatory via Peppol since January 2026), the buyer typically publishes its Peppol identifier as a Belgian VAT number under schemeId: "9925":
"buyer": {
"name": "Federale Overheidsdienst Voorbeeld",
"vatId": "BE0123456789",
"peppol": { "schemeId": "9925", "id": "BE0123456789" },
"address": { ... }
}Cross-border B2B
For an EU-cross-border invoice (FR seller, NL buyer), provide each party’s local EAS:
"seller": {
"name": "Acme SARL",
"vatId": "FR12345678901",
"peppol": { "schemeId": "9957", "id": "FR12345678901" },
...
},
"buyer": {
"name": "Voorbeeld B.V.",
"vatId": "NL123456789B01",
"peppol": { "schemeId": "9944", "id": "NL123456789B01" },
...
}Reusing a Peppol invoice for an XRechnung buyer
If your buyer is a German B2G recipient that requires XRechnung, switch the request to standard: "xrechnung" (Beliq picks profile: "xrechnung" by default). The invoice payload is otherwise identical.