Skip to content
Back to News
Changelog

SDK 4.4.0 — a Peppol scheme has two spellings, and the XML only accepts one

sdkpeppolfix

If you send from the United Kingdom, Ireland or Germany and you build UBL with this SDK, upgrade. Documents you generated before 4.4.0 were rejected by the network, and the two causes are independent — fixing one alone was not enough.

A scheme has two names, and one of them contains a colon

The official Peppol code list publishes every scheme twice: under its numeric EAS code (9932) and under a symbolic form (GB:VAT). They are two names for the same scheme. 98 of the 105 entries contain a colon in that symbolic form — the same character that separates a scheme from its value.

So GB:VAT:123456789 cannot be split by position, and we were splitting it by position:

GB:VAT:123456789   →   schemeID="GB"     value="VAT:123456789"    ⛔ both wrong

GB is not in the CEF EAS code list, so BR-CL-25 failed and the document was refused. This was not an exotic input: GB:VAT is the only code our own SCHEMES_BY_COUNTRY table offers for the United Kingdom. Anyone following our guidance hit it.

Both spellings are now accepted anywhere a peppolId is taken, and always resolved to the numeric code the network requires. The same flaw existed independently in directory.lookup(), which looked participants up under the wrong scheme; it is fixed too.

// All four are now equivalent — and all four produce schemeID="9932"
{ peppolId: "9932:123456789" }
{ peppolId: "GB:VAT:123456789" }
{ peppolId: "gb:vat:123456789" }
{ peppolId: " GB:VAT:123456789" }

The second cause: two neighbouring fields, two different code lists

Correcting the split still left those documents rejected, on a different rule.

BR-CL-25 governs cbc:EndpointID/@schemeID and judges against the CEF EAS list. BR-CL-10 governs cac:PartyIdentification/cbc:ID/@schemeID and judges against the ISO 6523 ICD list. They are not the same list, and they do not nest: 9932 (UK), 9935 (Ireland) and 9930 (Germany) are legal in the first and fatal in the second. We were writing the same scheme into both.

That field is optional, so it is now omitted when the scheme cannot legally carry it. ⚠️ Clearing BR-CL-10 is not the same as being conformant: dropping the seller identifier (BT-29) means BR-CO-26 now needs vatNumber (BT-31) or companyId (BT-30) to be present. validate() warns when neither is.

This one is worth dwelling on if you write your own tests: it hit the numeric spelling too, so no amount of testing different spellings would have surfaced it. Only running the builder's output through the real schematron did.

If you parse Peppol IDs yourself

Don't split on the first colon — and don't split on the last one either, since nothing guarantees a value has no colon. Resolve the prefix against the code list. parsePeppolId is now exported for exactly this:

import { parsePeppolId } from "@getpeppr/sdk";

parsePeppolId("GB:VAT:123456789");  // { scheme: "9932", id: "123456789" }
parsePeppolId("DE:LWID:04011000");  // { scheme: "0204", id: "04011000" }
parsePeppolId("0208:0685660237");   // { scheme: "0208", id: "0685660237" }

Also exported: canonicalScheme, lookupCanonicalScheme, ISO6523_ICD_CODES and isIso6523IcdCode.

Stricter input validation

A malformed peppolId is now a validation error on all three places that take one — buyer, seller and payee — instead of silently producing an invalid schemeID. That includes the case worth naming: the scheme code copied without its identifier ("GB:VAT", "DE:LWID"), which is exactly what our own guided-options table displays.

Not affected

Documents sent as JSON through POST /v1/invoices were never affected — the gateway builds that UBL itself. Nor was POST /v1/invoices/import, where you supply your own document. This fix concerns buildInvoiceXml, buildCreditNoteXml, toXml() and directory.lookup().