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 13 countries, providing vatId and address.countryCode is enough — Beliq picks the canonical EAS scheme automatically:
| Country | schemeId |
Scheme name | What id is |
|---|---|---|---|
| Austria | 9914 |
AT:VAT | VAT number (ATU…) |
| Belgium | 9925 |
BE:VAT | VAT number (BE…) |
| Denmark | 0184 |
DK:CVR | CVR registration number |
| Finland | 0213 |
FI:OVT | OVT identifier |
| 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 canonical OpenPeppol scheme expects a registry identifier (organisation number), not the VAT number. 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 |
|---|---|---|---|---|
| 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 Norwegian or Swedish recipient is supplied with vatId only, the engine fails closed with a clear error — silently stripping the MVA suffix or pivoting to a different scheme would risk producing an invalid Peppol endpoint.
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.
When validation passes, the /v1/validate response includes:
{
"valid": true,
"format": "ubl",
"profileDetected": "peppol",
"schematronVersion": "1.3.16",
"ciusVersion": "PeppolBIS-Billing-3.0.20",
"peppolVersion": "PeppolBIS-Billing-3.0.20",
"errors": [],
"warnings": []
}What Beliq does not do
Beliq generates and validates Peppol-compliant XML, but it does not transmit your invoice over the Peppol Network. To deliver an invoice to a buyer’s Peppol Access Point, hand the generated XML to your own Access Point provider (a list of certified providers is published on peppol.org).
This split keeps Beliq focused on the parts where compliance work is hardest — generating XML that is structurally and semantically correct on the first try — while letting you choose any Access Point partner.
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, and Factur-X 1.08 — see the format overview and DOCUMENT_TYPE_STANDARD_MISMATCH. Italy FatturaPA does not yet accept CreditNote payloads; setting documentType: "creditnote" with standard: "fatturapa" 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",
"seller": { /* … */ },
"buyer": { /* … */ },
"lines": [ /* … */ ]
}
}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 other generators (xrechnung, facturx, zugferd) 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.