← Blog

Integrations

Peppol UBL-quirks: 18 stille fouten uit een echte rollout

De creditnota van een 23-koppige installateur in Arnhem kreeg vrijdag 200 OK terug van het Access Point en verdween daarna in het ERP van de koper. Hier de achttien quirks die we erna vingen.

Jacob Molkenboer· Oprichter · A Brand New Company· 9 mei 2026· 9 min
Crèmekleurige envelop met donkergroene lakzegel, groen lint door de flap, messing label en rood stempel op linnen.

De creditnota kwam binnen bij het Access Point om 16:47 op een vrijdag in maart. De AS4-receipt kwam twee seconden later terug met 200 OK en een SignalMessage. Maandagochtend hing de controller van de 23-koppige installateur in Arnhem aan de lijn met de crediteurenadministratie van de hoofdaannemer. Geen creditering. Geen registratie. Geen bounce. De factuur die de creditnota corrigeerde stond als betaald in het grootboek van de koper, onaangeraakt.

De creditnota had het Access Point van de koper bereikt. Hij was door AS4-transport heen gekomen. En ergens tussen de Peppol-envelope en de Exact Globe-import van de koper was de cac:OrderReference eruit gestript, kon de matching engine de originele factuur niet vinden, en had een juniormedewerker hem in een 'te beoordelen'-queue gezet die niemand las.

Dat was quirk nummer één van achttien. De andere zeventien vingen we in de zes weken erna, tijdens de uitrol van een facturatie-agent die meekijkt naar de AP-receipts, de UBL door drie onafhankelijke schematron-rulesets laat lopen, en elk geval markeert waar het aantal velden tussen wat we verstuurden en wat de koper parste met meer dan nul verschilt. Hier is de cheatsheet, gerangschikt op hoe stil elke failure mode dingen sloopt.

Waarom 200 OK op Peppol geen bevestiging van aflevering is

Het vier-corner-model van Peppol laat het AP van de afzender het bericht via AS4 overdragen aan het AP van de ontvanger. De 200 OK op AS4 betekent dat het ontvangende AP de envelope heeft geaccepteerd en weggeschreven. Het betekent niet dat de UBL door schematron is gekomen, dat het ERP van de koper hem heeft geparset, of dat een mens hem ooit zal zien. Die kloof, tussen transport-acceptatie en business-acceptatie, is waar de meeste van deze quirks zitten.

Het OpenPeppol AS4-profiel is hier expliciet over in de AS4 Profile 2.0-documentatie. De Message Service Handler retourneert de receipt voordat de business-applicatie van de ontvanger de payload heeft aangeraakt. Behandel je de receipt als bewijs van aflevering, dan verstuur je blind.

Waarschuwing

Behandel de AS4-receipt als een transport-acknowledgement, niet als een bevestiging van aflevering. De 200 OK leeft op de verkeerde laag om te weten of het ERP van de koper je document heeft geparset, geaccepteerd of stilletjes velden heeft laten vallen bij de import.

Vijf stille strips die traceerbaarheid slopen

Dit zijn de ergste. Het Access Point geeft een schone receipt, het ERP van de koper importeert het document, en een veld dat je nodig had is weg. Geen logregel aan beide kanten benoemt het verlies.

1. OrderReference verdwijnt op gedeeltelijke creditnota's

UBL staat cac:OrderReference toe op een CreditNote. Sommige AP-implementaties normaliseren het document, zien dat de bovenliggende cac:OrderReference één lege cbc:SalesOrderID omhult (omdat een gedeeltelijke creditering er niet altijd één heeft) en snijden het hele element weg, inclusief de cbc:ID die we nodig hadden voor de koper-matching. Vul beide children altijd expliciet, of zet een niet-lege default voor SalesOrderID.

2. GLN-buyer identifier valt weg boven €25.000 bij ViewPoint-Bouw-overdracht

Deze kostte ons een week om te reproduceren. De EDI-overdracht van ViewPoint-Bouw stuurt facturen boven een instelbare drempel door een tweede validatieslag die het buyer party-blok her-serialiseert. De her-serializer behoudt cbc:EndpointID schemeID="0088" maar laat cac:PartyIdentification/cbc:ID schemeID="0088" vallen wanneer de GLN-waarde identiek is aan de EndpointID. Het ERP van de koper joint op PartyIdentification, niet op EndpointID. Gevolg: factuur komt aan, leverancier-match mislukt, document belandt in een handmatige queue.

3. Voorloopnullen in PaymentID gestript tijdens canonicalisatie

Een payment reference van 0000000000123456 komt binnen bij een AP dat whitespace en numerieke strings canonicaliseert als onderdeel van zijn XML-handtekening-pipeline. De referentie staat nu als 123456. De bank-reconciliatie van de koper matcht op de 16-cijferige vorm. Drie facturen stonden een week ongematcht voor we het doorhadden.

4. Note-elementen ingekort voorbij 1024 tekens zonder waarschuwing

De NLCIUS-schematron geeft een warning op een lange cbc:Note, geen error. Verschillende APs laten hem door. Het ERP van de koper, geconfigureerd voor EN 16931-minima, kapt af op 1024 en schrijft de rest weg naar een neventabel die niemand bewaakt. Staat in je note de werkbon-detail, dan is die weg.

5. BillingReference valt weg als zijn parent-ordering-blok leeg is

Zelfde familie als quirk 1. De cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID van een creditnota is het ene veld dat hem terugkoppelt aan de originele factuur. Is de zuster cac:AdditionalDocumentReference leeg, dan snoeien sommige AP-normalizers de lege parent eruit en nemen ze BillingReference mee. De creditnota komt aan, de link naar het origineel is weg, en de AP-queue van de koper laat alleen 'niet-toegewezen' zien.

Vier gevallen waarin 200 OK voorafgaat aan een stille afwijzing

6. NLCIUS-schematron slaagt, lokale CIUS van koper weigert

Verschillende Nederlandse kopers draaien een strengere lokale CIUS bovenop NLCIUS, strakker op BTW-categoriecodes, kortingsredenen en contractreferentieformat. Het AP valideert tegen NLCIUS, geeft 200 OK terug, en daarna weigert het ERP van de koper stil op de strengere ruleset. Geen bounce bereikt de afzender, omdat de afwijzing voorbij de Peppol-laag gebeurt.

7. Versiedrift in schematron-rules tussen verzendend en ontvangend AP

Tussen januari en maart 2026 zagen we AP-paren die NLCIUS 1.0.3.5 (zender) tegen 1.0.3.6 (ontvanger) draaiden. Het versieverschil voegde een regel toe op cac:TaxCategory voor verlegde-BTW in de bouw. Zenders gaan door, ontvangers weigeren na receipt. De Peppol BIS Billing 3.0-docs publiceren de canonieke ruleset; pin je validatie op de versie die je kopers draaien, niet op wat je AP meelevert.

8. Dubbele ID geaccepteerd met verschillende bedragen

Verstuur twee facturen met dezelfde cbc:ID en verschillende cbc:PayableAmount. Beide geven 200 OK terug. Sommige APs dedupliceren, andere niet. Het ERP van de koper pakt of de eerste, of de laatste, of importeert beide en laat een medewerker kiezen. Geen van die uitkomsten is goed, en de afzender heeft geen zicht op wat er gebeurde.

9. Verlegde BTW verkeerd gelezen als nultarief

De bouw in NL gebruikt BTW-verlegd. De UBL-categoriecode is AE. Sommige koper-ERPs die niet zijn bijgewerkt lezen AE als alias voor nultarief en boeken de regel BTW-vrij in plaats van als verlegd. De schematron-slag van het AP vangt dit niet; het is een downstream parser-bug, maar de factuur 'slaagt' met de verkeerde fiscale behandeling.

Vijf mismatches tussen schematron en validator

10. AllowanceCharge op documentniveau versus regelniveau

EN 16931 staat beide toe. NLCIUS staat beide toe. Sommige koper-ERPs honoreren alleen regelniveau. Een korting op documentniveau van €450 verdwijnt; de regeltotalen tellen niet op tot het documenttotaal in hun grootboek en de import faalt met een generieke 'totalen kloppen niet' die de importer logt en niemand leest.

11. InvoicePeriod op regel overrulet document, behalve wanneer dat niet zo is

UBL staat cac:InvoicePeriod per regel toe. Sommige ontvangers negeren regel-niveau-periodes en gebruiken de documentperiode voor omzetverantwoording. Meng je maandelijkse onderhoudscontract-regels met eenmalige installatie-regels, dan landt de installateursomzet in de verkeerde periode en lijken de accruals van de koper een maand af.

12. Verwarring tussen BuyerReference en OrderReference bij kleinere kopers

Ruwweg de helft van de Nederlandse koper-ERPs die we aanraakten routeert op cbc:BuyerReference (een vrije string die de koper zet voor zijn eigen AP-routing). De andere helft routeert op cac:OrderReference/cbc:ID. Doe je dit fout, dan belandt je factuur in andermans projectmap zonder error en zonder notificatie.

13. Inconsistentie in schemeID van EndpointID

NL: 0106 voor KvK, 0190 voor OIN, 0088 voor GLN. We zagen drie kopers in twee maanden bij wie de geconfigureerde EndpointID-schemeID niet overeenkwam met wat hun AP adverteerde in SMP. Stuur 'm naar 0106, hun AP routeert alleen 0190, het AP geeft 200 OK en zet hem in een handmatige triage-queue die misschien wel, misschien niet wordt opgepakt.

14. Limieten voor ingebedde PDF-bijlagen verschillen per AP

UBL heeft geen gedocumenteerde harde limiet op cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject. APs zijn het in de praktijk oneens. We hebben 5 MB, 10 MB en 20 MB gezien als praktische plafonds. Boven het plafond van het AP weigeren sommige (goed), strippen andere de bijlage en forwarden zonder (slecht).

Vier ViewPoint-Bouw-extensies die drift vertonen

De Nederlandse bouwsector stapelt zijn eigen EDI-eisen bovenop Peppol via ViewPoint-Bouw en de naburige BouwConnect-endpoints. Dat gaat goed wanneer beide kanten identiek geconfigureerd zijn, en lelijk als dat niet zo is.

15. ProjectReference valt weg als schemeID ontbreekt

Het bouwprofiel vereist cac:ProjectReference/cbc:ID met een schemeID die de projectnummerings-autoriteit identificeert. Laat je de schemeID weg, dan laten de meeste APs hem door; het ontvangende bouw-ERP weigert post-import en mailt een medewerker aan de koper-kant, niet de afzender. De afzender ziet 200 OK en gaat ervan uit dat de projectref is aangekomen.

16. ContractDocumentReference voor raamovereenkomsten verliest typecode bij her-serialisatie

Raamovereenkomsten gebruiken cac:ContractDocumentReference met een typecode. De ViewPoint-Bouw-her-serializer aan koper-kant behoudt de ID maar laat de typecode vallen, waarna hun ERP hem behandelt als eenmalige order. Periode-facturatie breekt. De fix zit aan de kant van de koper, maar je moet weten dat je moet escaleren.

17. Onderaanneming-party-blok genegeerd bij kleinere koper-ERPs

De bouwextensie draagt een party-blok voor onderaanneming, met het oog op ketenaansprakelijkheid. Kleinere koper-ERPs modelleren dat niet en laten het stil vallen bij import. Voor de installateur telt dit, omdat het AP van de hoofdaannemer downstream doorstuurt naar een keten-aansprakelijkheidstracker die afhankelijk is van de aanwezigheid van het blok.

18. Round-trip XML-normalisatie herordent AllowanceCharge-children

UBL is element-volgorde-gevoelig. De ViewPoint-Bouw-her-serializer herordent in oudere versies cac:AllowanceCharge-children alfabetisch. De downstream-schematron van de koper faalt dan op element-volgorde, maar de fout valt voorbij het AP, dus de afzender ziet 200 OK. Upgrade ViewPoint-Bouw of voer een schematron-precheck uit bij verzending.

Hoe we de agent hebben geïnstrumenteerd om dit te vangen

Dit stil vangen vereist meer dan een send-and-forget-pipeline. Elk uitgaand document wordt gevalideerd tegen drie rulesets voordat het AP ons in een vals gevoel van veiligheid 200-OK't, en een reconciliation job trekt 's ochtends AP-zijde en koper-zijde document-kopieën binnen om ze te diffen.

# Valideer de UBL die we gaan versturen tegen drie rulesets
# voordat het AP 200 OK teruggeeft op transport.
xmllint --schema UBL-Invoice-2.1.xsd invoice.xml --noout
saxon -s:invoice.xml -xsl:NLCIUS-1.0.3.6.xsl    -o:nlcius.svrl
saxon -s:invoice.xml -xsl:PEPPOL-BIS-3.0.xsl    -o:peppol.svrl
saxon -s:invoice.xml -xsl:BUYER-LOCAL-CIUS.xsl  -o:buyer.svrl

# Na verzenden: trek de door het AP afgeleverde kopie op en diff tegen wat we verstuurden.
curl -sS "$AP_API/messages/$MSG_ID/payload" -o delivered.xml
xmlstarlet c14n invoice.xml   > sent.canon.xml
xmlstarlet c14n delivered.xml > delivered.canon.xml
diff -u sent.canon.xml delivered.canon.xml || alert "silent strip on $MSG_ID"

Eén check voordat je verstuurt

Heb je deze week maar tijd voor één extra check in je e-factuur-pipeline, voeg dan deze toe: trek na elk uitgaand document een kopie op van de UBL die het AP van de koper daadwerkelijk downstream afleverde (de meeste kopers kunnen die op verzoek geven) en diff hem tegen wat je verstuurde. Vergelijk aantallen elementen, attribuut-aanwezigheid op het buyer party-blok, en de waarden van cbc:ID op cac:OrderReference en cac:BillingReference. Komen die niet byte-voor-byte overeen, dan zit er ergens op het pad een stille strip.

Toen we de facturatie-agent voor de Arnhemse installateur bouwden, was de verrassing niet dát er quirks waren (Peppol is een federatief netwerk, en federatie kweekt quirks), maar dat bijna allemaal voorbij de AP-receipt zaten, in de kloof tussen transport-geaccepteerd en business-geaccepteerd. We zijn een nachtelijke reconciliatie gaan draaien die AP-zijde en koper-zijde document-tellingen en veld-diffs in hetzelfde dashboard trekt. De week dat we live gingen, stopte de controller met bellen naar de hoofdaannemer. Dat werk hoort bij hoe we AI-agents bouwen voor operations-teams die geld verschuiven.

Kern

Op Peppol arriveert de 200 OK voordat het ERP van de koper je UBL aanraakt. Stille veld-strips op gedeeltelijke crediteringen en GLN-identifiers gebeuren in die kloof.

FAQ

Betekent een 200 OK van een Peppol Access Point dat mijn factuur is afgeleverd?

Nee. AS4 geeft 200 OK terug zodra het ontvangende AP de envelope wegschrijft. Het ERP van de koper heeft hem dan nog niet aangeraakt, en veel stille fouten gebeuren voorbij dat punt.

Waarom verdwijnt OrderReference op gedeeltelijke creditnota's?

Sommige AP-implementaties normaliseren UBL en snoeien parent-elementen met lege children weg. Omhult cac:OrderReference een lege SalesOrderID, dan kan het hele element inclusief cbc:ID gestript worden.

Wat is NLCIUS en hoe verhoudt het zich tot Peppol BIS Billing 3.0?

NLCIUS is de Nederlandse Core Invoice Usage Specification, een nationale aanscherping van EN 16931 die door Simplerinvoicing en de Nederlandse Peppol Authority wordt gebruikt. Het draait naast Peppol BIS Billing 3.0-validatie.

Hoe detecteer ik stille veld-strips op het Peppol-netwerk?

Trek na elke verzending de UBL op die het AP van de koper downstream afleverde en diff hem tegen wat je verstuurde. Vergelijk aantallen elementen, attribuut-aanwezigheid en ID-waarden. Mismatches op byte-niveau wijzen op een strip.

integrationsai agentsautomationcase studyoperationsworkflow

Iets bouwen?

Start een project