eliDocs
Format Reference

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:AccountingSupplierPartycbc:EndpointID Seller Peppol electronic-address with @schemeID Required by PEPPOL-EN16931-R020
cac:AccountingCustomerPartycbc: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:

  1. XSD — UBL 2.1 structural validity
  2. EN 16931 — common European business rules (BR-01, BR-CO-, BR-S-, etc.)
  3. 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.