Peppol e-invoicing is becoming mandatory for B2G (business-to-government) transactions in the Netherlands, and it is increasingly expected in B2B as well. Most businesses know they need to get compliant, but few understand what is actually involved on the technical side. The information out there tends to be either too high-level ("just connect to Peppol") or buried in specification documents that assume you already know the ecosystem.
This article covers the practical reality of implementing Peppol e-invoicing: what the network actually is, what your systems need to support, how to connect, and where implementations typically go wrong.
What Peppol Actually Is
The first misconception to clear up: Peppol is a network, not a file format. It stands for Pan-European Public Procurement OnLine, and it defines how electronic documents get exchanged between parties. Think of it like email for business documents: you do not send invoices directly to your customer, you send them through a network of intermediaries.
Peppol uses a 4-corner model:
- Corner 1 -- the sender (you, the supplier)
- Corner 2 -- the sender's Access Point (your Peppol service provider)
- Corner 3 -- the receiver's Access Point (your customer's Peppol service provider)
- Corner 4 -- the receiver (your customer)
Your invoice goes from your system to your Access Point, which looks up the receiver's Access Point in the Peppol SMP (Service Metadata Publisher) directory, routes the document there, and the receiver's Access Point delivers it to the end recipient. You never connect directly to your customer's system.
The document format used within Peppol is UBL (Universal Business Language), specifically UBL 2.1 XML. The invoice specification follows the PINT (Peppol International) model, which has replaced the older BIS Billing 3.0 in newer implementations. In the Netherlands, the NLCIUS (Netherlands Core Invoice Usage Specification) further constrains the UBL format with Dutch-specific rules.
To participate, every party needs a Peppol Participant ID. In the Netherlands, these are typically registered through SIMPLERINVOICING (the Dutch Peppol authority) or through a certified Access Point provider who handles the registration on your behalf.
What Your ERP Needs to Support
Whether you use NetSuite, SAP, Exact, or any other ERP, the core requirement is the same: your system needs to produce valid UBL 2.1 XML from your invoice data. This is where the complexity lives.
Field mapping
Every field in your ERP's invoice record needs to map to the correct UBL element. The straightforward fields -- invoice number, date, line items, amounts -- map easily enough. The problems start with the fields that are specific to Peppol and Dutch tax requirements:
- EndpointID -- the Peppol participant identifier for both sender and receiver. This is not the same as a VAT number, though it can be derived from one. The scheme identifier (e.g.,
0106for Dutch KvK numbers,0190for Dutch VAT) must be correct. - Tax category codes -- UBL uses a specific set of tax category codes (S for standard rate, Z for zero-rated, AE for reverse charge, etc.). Your ERP's internal tax codes rarely match these directly.
- KvK number -- the Dutch Chamber of Commerce registration number, required as a party identification element with scheme
0106. - BTW (VAT) identification -- must follow the format
NL+ 9 digits +B+ 2 digits, placed in the correctPartyTaxSchemeelement. - Payment means code -- specifies how the invoice should be paid (30 for bank transfer, 58 for SEPA credit transfer). Your IBAN and BIC go in the
PaymentMeansblock.
Common ERP mapping errors
From implementations I have done across NetSuite, Exact Online, and custom systems, the same mapping errors come up repeatedly. Scheme identifiers get omitted or use the wrong value. Tax categories default to S even when the transaction is reverse-charge or intra-community. The OrderReference field is left empty when the buyer's purchase order number is actually required by the receiving party. Line-level tax calculations do not round consistently with the invoice-level totals, causing validation failures on the TaxTotal element.
The UBL specification is precise about calculation consistency. If your line-level tax amounts do not sum to exactly the invoice-level tax total -- down to the cent -- the invoice will be rejected.
Connecting to an Access Point
You do not connect to "Peppol" directly. You connect to a certified Access Point (AP) provider, which handles the actual network communication. In the Netherlands, several certified APs operate:
- Storecove -- popular with international companies, strong API
- Tradeshift -- enterprise-focused, broad ERP integrations
- Billit -- common in Benelux region
- Kolofon -- Dutch provider, focused on smaller businesses
- Your ERP vendor -- some (like Exact) have built-in Peppol connectivity
Integration patterns
Most Access Points offer REST API integration. The typical pattern works like this:
- Sending: your system generates the UBL XML, POSTs it to the AP's API endpoint, and receives a delivery tracking ID in response.
- Receiving: the AP provides a webhook URL that you register. When an invoice arrives for your Peppol ID, the AP calls your webhook with the UBL document. Alternatively, you can poll the AP's inbox endpoint on a schedule.
- Status tracking: after sending, you poll the AP's status endpoint (or receive webhook notifications) for delivery confirmations, rejections, or errors from the receiving side.
The API integration itself is usually straightforward -- a few REST calls. The complexity is in producing valid UBL and handling all the edge cases on the receiving side: parsing incoming UBL back into your ERP's data model, matching invoices to purchase orders, handling credit notes, and dealing with documents that use different PINT or BIS versions.
Common Implementation Mistakes
Having done this for multiple Dutch companies, certain mistakes repeat across nearly every project.
Not validating before sending
Peppol validation is strict. The NLCIUS specification defines a large set of business rules (prefixed with BR-NL) on top of the base PINT rules. If you send an invoice that violates any of these, the receiving Access Point will reject it. Always validate your UBL against the NLCIUS schematron rules before submitting to your Access Point. Most APs offer a validation endpoint -- use it during development and keep using it in production as a pre-flight check.
Ignoring the receiving side
Teams often focus entirely on sending invoices and forget that Peppol is bidirectional. Once you register a Peppol participant ID, other parties can send you invoices through the network. You need a receiving pipeline: webhook handler, UBL parser, mapping into your ERP's accounts payable module, and a workflow for exceptions when incoming invoices do not parse cleanly. If you do not build the receiving side, incoming invoices will queue up at your Access Point, and you will not know they are there.
Incorrect tax handling for cross-border
Dutch companies that trade within the EU often get tax handling wrong. An intra-community supply to a Belgian company requires tax category code K (intra-community supply) and a zero tax amount, not category S with 0% rate. Reverse charge for services uses category AE. Getting these wrong does not just cause Peppol rejections -- it creates tax compliance issues.
Treating it as a one-time project
Peppol specifications evolve. The transition from BIS Billing 3.0 to PINT is one example. Scheme identifiers get updated. New business rules get added to the NLCIUS. Your Access Point may change their API. If you treat Peppol as a "build it and forget it" project, you will eventually start getting rejections when something changes upstream. Build monitoring into your implementation from day one: track rejection rates, validation errors, and delivery failures.
How I Implement It
My typical Peppol implementation follows a consistent pattern, refined across multiple Dutch companies:
- Audit the current invoice flow. I map out where invoices originate, what data is available at the point of creation, and what the current output looks like (PDF, email, EDI). This identifies gaps in the data that UBL will require.
- Design the mapping layer. I build a dedicated transformation layer between your ERP and the UBL output. This is not a simple field-to-field copy -- it handles tax category logic, scheme identifier lookups, rounding rules, and conditional fields based on transaction type (domestic, intra-EU, reverse charge).
- Connect to the Access Point API. I integrate with the chosen AP's REST API for both sending and receiving, including webhook handlers, retry logic for failed deliveries, and status tracking.
- Validate with test invoices. Every Access Point has a test environment. I run a full set of test invoices covering each transaction type: standard domestic, intra-EU, reverse charge, credit notes, and invoices with different payment terms. Each one gets validated against NLCIUS schematron before sending.
- Go live with monitoring. Once tests pass, we switch to production with monitoring on rejection rates, delivery times, and incoming invoice volumes. The first two weeks typically need some fine-tuning as real-world invoices hit edge cases the test set did not cover.
For a standard implementation -- one ERP, one Access Point, domestic and intra-EU invoicing -- this typically takes two to four weeks. More complex setups with multiple entities, custom ERP workflows, or high invoice volumes take longer, but the approach stays the same.