The guideChapter 03
Participant identifiers
A scheme and a value, the UK codes that matter, and why the same company can exist twice on the network.
An email address is a string somebody tells you. A Peppol identifier is a pair: a scheme, saying what kind of identifier this is, and a value, the identifier itself. Written down it looks like this:
9932:GB123456789
9932 is the scheme — a UK VAT number — and GB123456789 is the value.
The scheme codes come from the EAS list, an official code list maintained
for exactly this purpose. Without the scheme the value is ambiguous: plenty
of countries issue numbers that look alike, and the network has to know
which register it is being pointed at.
The codes a UK developer meets
9932— UK VAT number. The usual identifier for a UK business.0088— GLN, a GS1 global location number. Common in retail supply chains, and what the NHS uses.0060— DUNS number, occasionally seen with larger multinationals.
And one absence that catches people out: there is no scheme for a Companies House number. A company registration number is the obvious identifier for a UK business and it is not how Peppol addresses one. If your data model treats the company number as the primary key for a counterparty — which is reasonable for every other purpose — you will need somewhere to keep a Peppol identifier alongside it.
Scheme 0192, which turns up in search results as a “national organisation
number”, is Norwegian. It is not a UK code.
The same company, twice
UK VAT numbers are written both with and without the GB prefix, and both
forms exist as registrations on the network. Peppol treats them as
different participants: 9932:GB123456789 and 9932:123456789 hash to
different addresses, resolve through different DNS lookups, and can have
different Access Points behind them — or one can exist while the other does
not.
There is no way to reason your way around this from first principles. The
reliable approach is to try both forms and use whichever answers, which is
what GET /v1/participants/{id} does, reporting back the form that is
actually registered. If you implement lookups yourself, do the same, and
store the form that answered rather than the form you were given.
Identifiers are matched case-insensitively, and the network works in
lower case because the identifier is hashed on the way into the SML. Expect
a lookup to return gb123456789 where you sent GB123456789; that is
normal, not a mangled value.
Looking one up
A lookup answers two questions at once: is this participant on the network, and what can they receive?
curl https://api.einvoicing.dev/v1/participants/9932:GB123456789 \
-H "Authorization: Bearer $EINVOICING_API_KEY"
Three things about the answer are worth designing around.
Not registered is an answer, not an error. A participant who is not on
the network comes back as a successful response saying registered: false.
Absence from a public register is a fact about the world; treating it as a
404 pushes application logic into exception handling, where it tends to
turn into “something went wrong” in front of a user who needed to be told
“this customer cannot receive e-invoices yet”.
A missing directory entry means nothing. The Peppol Directory is
optional for participants, so directory comes back null for plenty of
businesses that are registered and reachable. Only the SML and SMP are
authoritative. This is the single most common way a home-grown lookup goes
wrong.
Capabilities matter as much as existence. The response lists the document types the participant accepts. A business registered to receive orders but not invoices is registered — and will still reject your invoice. Check for the document type you are about to send, not merely for the participant.
What to do in your own code
- Store the scheme and the value separately, or store the identifier in
its
scheme:valueform, but never store a bare number and reconstruct the scheme later from a country field. That is how a GLN becomes a VAT number in a report six months on. - Ask for it. The simplest way to learn a counterparty’s Peppol identifier is to ask them for it during onboarding, next to the VAT number you already collect.
- Look it up before you send, and cache the result. Registrations
change rarely, and the lookup is a live network call: einvoicing.dev
caches for up to five minutes and tells you when it last asked, in
checked_at. - Re-check when sending fails. A participant who moves Access Point keeps the same identifier, so a cached endpoint can go stale while the identifier stays right.
With an identifier that resolves and a document type the receiver accepts, what is left is the document itself — which is where the rules live, and the next chapters.
Checked against its sources on 17 September 2026
One email when the guide is finished, and an explainer on Budget day.
Getting a key, validating a document, and the API reference.