Skip to main content

Peppol Identifiers

Every participant on the Peppol network has a unique identifier. Understanding how these identifiers work is essential for successful document exchange.

Identifier Structureโ€‹

A Peppol identifier consists of two parts:

{scheme}:{identifier}

For example:

  • 0106:12345678 โ€” Netherlands Chamber of Commerce (KVK) number
  • 9930:DE123456789 โ€” German VAT number
  • 0009:12345678900017 โ€” French SIRET number

Common Identifier Schemesโ€‹

The scheme code decides what the value means, so the wrong code makes an otherwise correct number unusable. Every name below is taken verbatim from OpenPeppol's published Electronic Address Scheme (EAS) code list, which is the only authority for what a code means. The tables cover the codes you are most likely to meet in Europe; the published list has 83.

The format of the value is set by whichever register or tax authority issues it, not by Peppol. This page states a format only where GoRoute itself enforces one โ€” see Format Rules below โ€” rather than guessing the rest.

European Schemesโ€‹

SchemeOfficial nameIdentifies
0007Organisationsnummer (Swedish legal entities)๐Ÿ‡ธ๐Ÿ‡ช Sweden
0088Global Location Number (EAN/GLN)๐ŸŒ Global
0096The Danish Business Authority โ€” P-number (DK:P)๐Ÿ‡ฉ๐Ÿ‡ฐ Denmark, a production unit
0106Association of Chambers of Commerce and Industry in the Netherlands (KVK)๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands
0130Directorates of the European Commission๐Ÿ‡ช๐Ÿ‡บ EU institutions
0184The Danish Business Authority โ€” CVR-number (DK:CVR)๐Ÿ‡ฉ๐Ÿ‡ฐ Denmark, the company itself
0190Organisatie-identificatienummer (OIN)๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands, public bodies
0192Organisasjonsnummer๐Ÿ‡ณ๐Ÿ‡ด Norway
0195Singapore Nationwide E-Invoice Framework (UEN)๐Ÿ‡ธ๐Ÿ‡ฌ Singapore
0196Icelandic identifier (kennitala)๐Ÿ‡ฎ๐Ÿ‡ธ Iceland
0198The Danish Business Authority โ€” SE-number (DK:SE)๐Ÿ‡ฉ๐Ÿ‡ฐ Denmark, a VAT registration
0200Legal entity code (Lithuania)๐Ÿ‡ฑ๐Ÿ‡น Lithuania
0201Codice Univoco Unitร  Organizzativa iPA๐Ÿ‡ฎ๐Ÿ‡น Italy, public bodies
0204Peppol-Leitweg-ID๐Ÿ‡ฉ๐Ÿ‡ช Germany, public bodies
0208Numero d'entreprise / ondernemingsnummer (CBE/KBO)๐Ÿ‡ง๐Ÿ‡ช Belgium
0209GS1 identification keys๐ŸŒ Global
0210Codice Fiscale๐Ÿ‡ฎ๐Ÿ‡น Italy
0211Partita IVA๐Ÿ‡ฎ๐Ÿ‡น Italy
0216OVTcode๐Ÿ‡ซ๐Ÿ‡ฎ Finland
0221The registered number of the qualified invoice issuer๐Ÿ‡ฏ๐Ÿ‡ต Japan
0230National e-Invoicing Framework (Malaysia)๐Ÿ‡ฒ๐Ÿ‡พ Malaysia
Three codes that are easy to get the wrong way round

0204 is the German Leitweg-ID, not a German VAT number โ€” German VAT is 9930. 0209 is GS1, not the Leitweg-ID. 0211 is the Italian Partita IVA, not an IPA code โ€” the Italian public-administration code is 0201.

VAT-based Schemesโ€‹

These are the 99xx codes. Most of them are VAT numbers, but not all: three that this page used to list as national VAT schemes are something else entirely, and are named here as what they are.

SchemeOfficial nameIdentifies
9910Hungary VAT number๐Ÿ‡ญ๐Ÿ‡บ Hungary
9914ร–sterreichische Umsatzsteuer-Identifikationsnummer๐Ÿ‡ฆ๐Ÿ‡น Austria
9918S.W.I.F.T. โ€” a bank identifier, not a VAT scheme๐ŸŒ Global
9919Kennziffer des Unternehmensregisters โ€” the Austrian company-register number, not a VAT scheme๐Ÿ‡ฆ๐Ÿ‡น Austria
9920Agencia Espaรฑola de Administraciรณn Tributaria๐Ÿ‡ช๐Ÿ‡ธ Spain
9922Andorra VAT number๐Ÿ‡ฆ๐Ÿ‡ฉ Andorra
9923Albania VAT number๐Ÿ‡ฆ๐Ÿ‡ฑ Albania
9924Bosnia and Herzegovina VAT number๐Ÿ‡ง๐Ÿ‡ฆ Bosnia and Herzegovina
9925Belgium VAT number๐Ÿ‡ง๐Ÿ‡ช Belgium
9926Bulgaria VAT number๐Ÿ‡ง๐Ÿ‡ฌ Bulgaria
9930Germany VAT number๐Ÿ‡ฉ๐Ÿ‡ช Germany
9931Estonia VAT number๐Ÿ‡ช๐Ÿ‡ช Estonia
9944Netherlands VAT number๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands
9957French VAT number๐Ÿ‡ซ๐Ÿ‡ท France
9959US Employer ID Number โ€” not a VAT scheme๐Ÿ‡บ๐Ÿ‡ธ United States

Codes this page used to list that the EAS list no longer carries. Six codes that earlier revisions of this page presented as current have been withdrawn from the EAS list. They are kept here, labelled, rather than deleted, because you will still find them in older integrations and in counterparties' existing registrations.

SchemeWhat it was used forOn the current list
0213Estonian registry codeEstonian participants appear as 9931 (VAT number)
0215Finnish Y-tunnus0216 (OVTcode) is the Finnish code on the list
9906Italian VAT, IT + 11 digits0211 (Partita IVA)
9917Dutch VAT9944 (Netherlands VAT number)
9955Swedish VATNo Swedish VAT code is on the list; use 0007 (Organisationsnummer)
9956Belgian VAT, BE + 10 digits0208, the enterprise number, which Belgium mandates for participants a service provider registers

GoRoute's own invoice validation still accepts five of the six, so a document carrying one is not rejected for that reason alone. The exception is 0215: it is not in GoRoute's accepted scheme list, and an invoice addressed with it fails EDEC_001 ("Invalid Peppol participant scheme"). Before you rely on any of the six, confirm which scheme the participant is actually registered under with participant lookup โ€” do not assume.

Validating Identifiersโ€‹

There is no identifier-validation endpoint

Earlier revisions of this page documented a GET route under /api/v1 that checked a scheme and identifier for format validity and returned valid, scheme_name and format_description. No such endpoint exists, and nothing in the API returns those fields.

Check identifier format in your own code against the format rules below. To find out whether an identifier is actually registered on the network, use participant lookup.

Identifier format is enforced only as part of validating a whole document โ€” for example the country layer's COUNTRY_AU_001 (ABN must be 11 digits) and COUNTRY_AU_002 (ABN checksum) on POST /api/v1/invoices/validate. There is no way to ask about an identifier on its own.

Looking Up Participantsโ€‹

Check whether a participant is registered on Peppol:

import requests

response = requests.get(
"https://app.goroute.ai/peppol-api/api/v1/participants/lookup",
params={"peppol_id": "0106:12345678"},
headers={"X-API-Key": "your_api_key"},
)

participant = response.json()

if participant["found"]:
print("Confirmed:", participant["participant_id"], participant["country"])
print("Can receive:", participant["capabilities"])
else:
# found: false means the network did not confirm the participant. That
# covers both "not registered" and "no registry answered", which this
# endpoint reports identically โ€” so do not tell anyone their identifier
# is wrong on the strength of it.
print("Not confirmed:", participant["message"])
One parameter, in scheme:value form

The lookup takes a single peppol_id (or its alias identifier) containing a colon โ€” 0106:12345678. Passing scheme and identifier as two separate parameters does not work; a value without a colon returns 400 with Provide peppol_id as scheme:value, e.g. 0248:OM1100099003.

Identifier Requirementsโ€‹

Format Rulesโ€‹

Each scheme has specific format requirements:

SchemeLengthFormatCheck Digit
01068Numeric onlyNo
01929Numeric onlyModulo 11
020810Numeric onlyModulo 97
993011DE + 9 digitsYes

Common Validation Errorsโ€‹

Common Mistakes
  • Including country prefix when not required (e.g., NL in KVK numbers)
  • Omitting country prefix when required (e.g., DE in German VAT)
  • Wrong number of digits
  • Including spaces or special characters

Best Practicesโ€‹

1. Validate Earlyโ€‹

Always validate identifiers before storing:

import re

# Format rules are yours to enforce โ€” see the table above.
SCHEME_FORMATS = {
"0106": re.compile(r"^\d{8}$"), # NL KVK
"0192": re.compile(r"^\d{9}$"), # NO Org.nr
"0208": re.compile(r"^\d{10}$"), # BE CBE
}


def is_well_formed(scheme: str, identifier: str) -> bool:
"""Check identifier format locally. There is no API for this."""
pattern = SCHEME_FORMATS.get(scheme)
return bool(pattern and pattern.match(identifier))


def network_confirmed(peppol_id: str) -> bool:
"""Whether the network confirmed the participant.

peppol_id must be in scheme:value form. This is not the same question as
"is this participant registered": False also comes back when no registry
could be reached, and the lookup reports the two identically. Use it to
decide whether to send, never to tell someone their identifier is wrong.
"""
response = requests.get(
f"{API_BASE}/api/v1/participants/lookup",
params={"peppol_id": peppol_id},
headers={"X-API-Key": API_KEY},
)
return response.json().get("found", False)

A False from network_confirmed is a reason to hold the document and try again later. See Participant Lookup for why the two cases cannot be told apart.

2. Normalize Before Storageโ€‹

Store identifiers in a consistent format:

def normalize_identifier(scheme: str, identifier: str) -> str:
"""Normalize identifier for storage."""
# Remove spaces and convert to uppercase
identifier = identifier.replace(" ", "").upper()

# Remove common separators
identifier = identifier.replace("-", "").replace(".", "")

return identifier

3. Use the Correct Schemeโ€‹

Different schemes may cover the same organization:

# A Dutch company might be registered with:
# - 0106 (KVK number) - Preferred for Peppol
# - 0190 (OIN) - Government organizations
# - 9944 (Netherlands VAT number) - the VAT scheme on the current EAS list
# (9917 is the withdrawn predecessor; older registrations still use it)

# Always check which scheme the receiver is registered with

Scheme Selection Guideโ€‹

When should you use which scheme?

CountryB2BB2GRecommended
๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands010601900106 (KVK)
๐Ÿ‡ฉ๐Ÿ‡ช Germany993002049930 (VAT) for business buyers; the Leitweg-ID (0204) your public-sector buyer gives you
๐Ÿ‡ง๐Ÿ‡ช Belgium020802080208 (enterprise number) โ€” the scheme Belgium requires for participants GoRoute registers
๐Ÿ‡ซ๐Ÿ‡ท France000900090009 (SIRET)
๐Ÿ‡ฎ๐Ÿ‡น Italy0210 or 021102010211 (Partita IVA) for business buyers; 0201 (Codice Univoco iPA) for public bodies
๐Ÿ‡ณ๐Ÿ‡ด Norway019201920192 (Org.nr) โ€” the only scheme Norway allows
๐Ÿ‡ธ๐Ÿ‡ช Sweden000700070007 (Org.nr)

API Referenceโ€‹

Validate identifierโ€‹

There is no such endpoint. See Validating Identifiers above.

Lookup participantโ€‹

GET /api/v1/participants/lookup

Requires the participants:read permission.

Parameters:

NameTypeRequiredDescription
peppol_idstringYes (or identifier)The full identifier in scheme:value form, e.g. 0106:12345678
identifierstringAccepted as an alias for peppol_idSame scheme:value form

A value with no colon returns 400.

Response:

{
"found": true,
"participant_id": "0106:12345678",
"name": "Example BV",
"country": "NL",
"capabilities": [
"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1"
],
"message": null
}

The response is flat โ€” there is no nested participant object, no registered_at, and the document types are in capabilities, not document_types. When the participant is not found, found is false and message explains why; the call still returns 200.

Next Stepsโ€‹