Automation MCP Server Features Blog Pricing Contact
Integration UBL Format

Peppol API Integration: What Your Access Point Checks, and What It Does Not

Every Peppol integration is two jobs wearing one name: producing a compliant Peppol BIS Billing 3.0 document, and moving it across the network. The access point owns the second job. This guide is about the first one: what actually gets checked at the access point's gate, what never does, why "Peppol API key" means two unrelated credentials, and how to catch every document-level rejection in development instead of in production traffic.

Searches for a "Peppol API" usually mean two different products, and most integration pain comes from not separating them. One layer moves documents: access points speaking AS4 to each other, participant lookup through SMP and SML, certificates issued under the OpenPeppol trust network. The other layer is the documents themselves: UBL 2.1 invoices conforming to Peppol BIS Billing 3.0, which is EN 16931 plus the network's own rule set on top. Your access point provider owns the first layer completely. The second layer is yours, and it is where nearly all rejections are born.

InvoiceXML sits on the document layer only: it creates and validates Peppol BIS 3.0, EHF, NLCIUS and PINT documents, and it does not transmit anything. That boundary is worth stating in the first paragraph because this guide will keep leaning on it: the cleanest Peppol architectures treat "build a compliant document" and "deliver it" as separate concerns with separate credentials, separate failure modes, and separate vendors.

Peppol is two jobs, not one

The Peppol eDelivery network standardizes how business documents travel: a four-corner model where the sender's access point (corner 2) looks up the receiver's access point (corner 3) via the SMP, wraps the document in a standard envelope, and delivers it over AS4. Everything about that layer, from the transport certificates to the participant directory, is infrastructure that certified access point providers operate. You do not build it, and unless you plan to become a certified provider yourself, you should not want to.

What travels inside the envelope is the document layer: for invoicing, a UBL 2.1 Invoice or CreditNote conforming to Peppol BIS Billing 3.0. BIS 3.0 is a CIUS of EN 16931, meaning it takes the European semantic standard and tightens it with network-specific rules: mandatory electronic addresses for both parties, a required buyer or purchase order reference, restrictions on which identifier code lists are allowed, and dozens of additional business rules with the PEPPOL-EN16931-* prefix.

The division of labour follows from that structure. The access point guarantees delivery to any registered participant. It does not, and cannot, produce your invoice: mapping your ERP's fields to EN 16931 business terms, choosing the right VAT category codes, addressing the buyer with the right identifier scheme, and passing the BIS rule set is document-layer work that happens before the network ever sees the file.


There is no single "Peppol API"

OpenPeppol maintains the specifications and certifies providers, but it does not operate a public API you can call to send an invoice, and there is no network-wide API key to obtain. In practice a "Peppol API integration" is a composition of two independent APIs:

Your access point provider's transmission API. Every provider wraps the network in its own REST interface, with its own authentication, its own endpoint for submitting documents, and its own way of reporting delivery status. This is where the transport-layer API key lives, and its shape varies completely from provider to provider.

A document API. Something has to turn your structured invoice data into a BIS-compliant UBL document and confirm that documents you are about to send, or have just received, actually pass the rules. That is the layer this site provides, with its own API key, and it works identically no matter which access point sits next to it.

Keeping the two separate is not just terminology hygiene. It keeps you portable: an integration that produces validated BIS 3.0 documents can switch access point providers by swapping the last HTTP call, because the document, which is the part the receiving side cares about, is provider-neutral. It also keeps testing sane, because the document layer can be exercised in full without a network contract, a test endpoint, or a single certificate.


What your access point checks

At submission time, a sending access point typically verifies four things:

That the receiver exists on the network. The provider resolves the receiver's participant identifier through the SMP lookup. If the buyer is not registered, or not registered for the document type you are sending, the submission fails regardless of how good the document is.

That the envelope is coherent. The participant identifiers, document type identifier and process identifier around the document must line up with what the receiver's SMP entry advertises.

That the document passes the BIS rule set. The network holds the sending access point responsible for the quality of what it forwards, so most providers run the official Peppol Schematron at the gate and reject documents that fail. This is the check integrators discover the hard way: an invoice their own XSD validation happily accepted bounces at the access point with a PEPPOL-EN16931-R003 or a code-list violation.

That the file is well-formed against the UBL schema. Malformed XML never gets as far as the rules.

Note when all this happens: at transmission time, in production, after your system has already booked the invoice as sent. The gate works, but it is the most expensive place in the whole pipeline to learn about a document problem.


What it does not check

The list of things the access point never looks at is longer, and it is the actual surface of a Peppol integration project:

Your field mapping. Whether your ERP's "customer reference" landed in BT-10 or got lost, whether the payment terms you meant to send are in the document at all, whether the line descriptions survived the export. The rules check structure and arithmetic, not intent.

Anything before you press send. The gate offers no development-time feedback. During the build phase you need the same verdict the access point will eventually deliver, on demand, in seconds, with rule identifiers you can act on, which is exactly what a validation endpoint is for.

The shape of your errors. When a provider does reject a document, the finding arrives in whatever format that provider chose, often a wrapped log line from the reference validator. Structured findings with a rule id, a plain-language message, and the XPath of the offending element are a document-layer service.

Received documents, for your purposes. The receiving access point verified that an inbound invoice passed the rules when it accepted delivery, but your AP workflow still needs the data out of the UBL and into your systems, and an independent check costs one call if a dispute makes provenance matter.

Building the document in the first place. The biggest non-check of all. The access point takes a finished BIS 3.0 file; producing one from raw invoice data, with correct VAT categories, totals that satisfy the calculation rules, and identifiers from the permitted code lists, was always your job.


The document layer in practice

The document half of the integration is one JSON request. POST /v1/create/ubl takes structured invoice data and returns a finished UBL 2.1 document built and validated against Peppol BIS Billing 3.0 (the default profile), with totals and the VAT breakdown computed from the line items:

curl -X POST https://api.invoicexml.com/v1/create/ubl \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "invoiceNumber": "INV-2026-0917",
      "issueDate": "2026-09-30",
      "currency": "EUR",
      "buyerReference": "PO-4711",
      "seller": {
        "name": "Nordwind Software GmbH",
        "vatIdentifier": "DE123456789",
        "electronicAddress": { "identifier": "DE123456789", "schemeId": "9930" },
        "postalAddress": { "line1": "Hauptstrasse 12", "postCode": "10115", "city": "Berlin", "country": "DE" }
      },
      "buyer": {
        "name": "Hanse Handel AG",
        "electronicAddress": { "identifier": "4012345000009", "schemeId": "0088" },
        "postalAddress": { "line1": "Speicherstadt 8", "postCode": "20457", "city": "Hamburg", "country": "DE" }
      },
      "lines": [
        {
          "quantity": 40,
          "unitCode": "HUR",
          "item": { "name": "Platform integration services" },
          "priceDetails": { "netPrice": 120.00 },
          "vatInformation": { "rate": 19, "categoryCode": "S" }
        }
      ]
    }
  }'

The response is the UBL file your access point's transmission API expects, already carrying the BIS customization identifier and already checked against the rule set that the gate will apply. If the request cannot yield a compliant document, the API refuses to build it and returns the violated rules as structured findings instead of a file, which moves the failure from the access point's queue to your development loop.

Two Peppol-specific requirements are enforced up front rather than discovered downstream: both parties must carry an electronic address (BT-34 and BT-49), and at least one of buyer reference (BT-10) or purchase order reference (BT-13) must be present, per PEPPOL-EN16931-R003. Real sample documents to compare against live on the UBL samples page.


Electronic addresses and EAS codes

The field that fails more first attempts than any other is the electronic address pair: an identifier plus a scheme code from the Electronic Address Scheme (EAS) code list, such as 0088 for a GLN, 9930 for a German VAT number, or 0208 for a Belgian enterprise number. The scheme tells the network how to interpret the identifier, and it is how participants are addressed for routing.

There is a trap in the code list itself. EN 16931 still carries legacy alphabetic entries (EM for email, AN, AQ, AS, AU), and documents using them validate as generic EN 16931, but Peppol's PEPPOL-EN16931-CL008 restricts the network to the numeric codes. An invoice addressed with schemeId: "EM" is valid EN 16931 and dead on arrival at the gate.

The create and validate endpoints pre-check exactly this: a missing scheme id on an electronic address is rejected with a field-level error, and a legacy alphabetic EAS code on a Peppol-bound profile is refused with a message naming numeric alternatives, before the Schematron even runs. The same check knows where it does not apply: XRechnung and NLCIUS validate against the broader EN 16931 code list, and EM is in fact the usual delivery scheme for XRechnung, so documents built for those profiles keep it.


Validate before the gate

For documents you did not create through the API (ERP exports, a legacy generator's output, inbound files whose provenance matters), POST /v1/validate/ubl runs the full stack the access point will apply: UBL XSD, the EN 16931 core rules, and the Peppol BIS overlay, returning every finding with its rule id, a plain-language description, and the XPath where it fired:

curl -X POST https://api.invoicexml.com/v1/validate/ubl \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "[email protected]"

Both valid and invalid documents return HTTP 200; branch on the valid flag. A typical pre-send wiring puts this call in two places: in CI, as a regression test over a folder of representative documents, and in the pipeline, as the last step before the file is handed to the access point's API. The first placement catches mapping regressions when they are introduced; the second guarantees that nothing your provider will bounce ever leaves your system, and produces an audit trail of verdicts you control rather than screenshots of a provider's error log.

The language guides walk this pattern through complete reception and issuance pipelines in C#, Java, Node.js, Python and PHP.


EHF, NLCIUS and PINT

BIS Billing 3.0 is the network's common denominator, but several markets route national flavours over the same infrastructure, and the same two-layer split applies to all of them. The request model stays identical; options.profile selects which rule set the document is built and validated against:

Profileoptions.profileWhere it applies
Peppol BIS Billing 3.0peppol-bis-3 (default)EU cross-border and most Peppol traffic, including Belgium's B2B mandate
EHF Billing 3.0ehfNorway (EHF details)
NLCIUSnlciusNetherlands (NLCIUS details)
PINTpint and country variantsPeppol International: Singapore, Australia, New Zealand, Japan, Malaysia (PINT details)
Generic EN 16931en16931Bilateral exchange outside the network, where the Peppol-only rules deliberately do not apply

The last row is the escape hatch worth knowing about: if a document is not headed for Peppol, building it as generic EN 16931 lifts the network-specific requirements, including the numeric-EAS restriction and the mandatory buyer reference. Choosing the profile is choosing which gate you are building for.


Get started

The two-layer split gives the integration a convenient property: the half covered here needs no access point to start. Create a document from your own data, validate one of your existing files, and you have exercised the entire document layer before any provider contract is signed. 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 Peppol UBL, EHF, NLCIUS, PINT, XRechnung, ZUGFeRD, Factur-X, and CII. Stateless processing, GDPR compliant by architecture, and callable from any stack with an HTTP client. It operates on the document layer and is not an access point.

Start free today

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.

GDPR Compliant No credit card required Setup in minutes
Peppol UBL
Factur-X
EN 16931
142 / 142 passed
Compliant
PDF/A-3 embedded