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 number
  • 0204:DE123456789 โ€” German VAT number
  • 9925:0123456789 โ€” French SIRET number

Common Identifier Schemesโ€‹

European Schemesโ€‹

SchemeCountryNameFormat Example
0007๐Ÿ‡ธ๐Ÿ‡ช SwedenOrganisationsnummer1234567890
0088๐ŸŒ GlobalEAN/GLN1234567890123
0096๐Ÿ‡ฉ๐Ÿ‡ฐ DenmarkCVR12345678
0106๐Ÿ‡ณ๐Ÿ‡ฑ NetherlandsKVK12345678
0130๐Ÿ‡ช๐Ÿ‡บ EUDirectorates-GeneralDIGIT.B.3
0184๐Ÿ‡ฉ๐Ÿ‡ฐ DenmarkCVR (alternative)DK12345678
0190๐Ÿ‡ณ๐Ÿ‡ฑ NetherlandsOIN00000001234567890000
0192๐Ÿ‡ณ๐Ÿ‡ด NorwayOrganisasjonsnummer123456789
0195๐Ÿ‡ธ๐Ÿ‡ฌ SingaporeUENS12345678A
0196๐Ÿ‡ฎ๐Ÿ‡ธ IcelandKennitala1234567890
0198๐Ÿ‡ฉ๐Ÿ‡ฐ DenmarkERSTORG12345678
0200๐Ÿ‡ฑ๐Ÿ‡น LithuaniaCompany Code123456789
0201๐Ÿ‡ฑ๐Ÿ‡น LithuaniaVATLT123456789
0204๐Ÿ‡ฉ๐Ÿ‡ช GermanyVATDE123456789
0208๐Ÿ‡ง๐Ÿ‡ช BelgiumEnterprise Number0123456789
0209๐Ÿ‡ฉ๐Ÿ‡ช GermanyLeitweg-ID991-12345-67
0210๐Ÿ‡ฎ๐Ÿ‡น ItalyCodice Fiscale12345678901
0211๐Ÿ‡ฎ๐Ÿ‡น ItalyIPA CodeABC123
0213๐Ÿ‡ช๐Ÿ‡ช EstoniaRegistry Code12345678
0215๐Ÿ‡ซ๐Ÿ‡ฎ FinlandY-tunnus1234567-8
0216๐Ÿ‡ซ๐Ÿ‡ฎ FinlandOVT003712345678
0221๐Ÿ‡ฏ๐Ÿ‡ต JapanCorporate Number1234567890123
0230๐Ÿ‡ฒ๐Ÿ‡พ MalaysiaBRN202301012345

VAT-based Schemesโ€‹

SchemeCountryFormat
9906๐Ÿ‡ฎ๐Ÿ‡น ItalyIT + 11 digits
9914๐Ÿ‡ฆ๐Ÿ‡น AustriaATU + 8 digits
9917๐Ÿ‡ณ๐Ÿ‡ฑ NetherlandsNL + 9 digits + B + 2 digits
9918๐Ÿ‡ช๐Ÿ‡ช EstoniaEE + 9 digits
9919๐Ÿ‡ช๐Ÿ‡ธ SpainESX + 8 digits
9920๐Ÿ‡ฆ๐Ÿ‡น AustriaAT + 9 digits
9922๐Ÿ‡ฆ๐Ÿ‡ฉ AndorraAD + 8 digits
9923๐Ÿ‡ฆ๐Ÿ‡ฑ AlbaniaAL + 10 digits
9924๐Ÿ‡ง๐Ÿ‡ฆ BosniaBA + 12 digits
9925๐Ÿ‡ซ๐Ÿ‡ท FranceFR + 11 digits (SIRET)
9926๐Ÿ‡ญ๐Ÿ‡บ HungaryHU + 8 digits
9930๐Ÿ‡ฎ๐Ÿ‡น ItalyIT + Partita IVA
9955๐Ÿ‡ธ๐Ÿ‡ช SwedenSE + 12 digits
9956๐Ÿ‡ง๐Ÿ‡ช BelgiumBE + 10 digits
9957๐Ÿ‡ซ๐Ÿ‡ท FranceFR + 11 digits
9959๐Ÿ‡ช๐Ÿ‡บ EUEmployer ID Number

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("Found:", participant["name"], participant["country"])
print("Can receive:", participant["capabilities"])
else:
print("Not on the Peppol network:", 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
020411DE + 9 digitsYes
020810Numeric onlyModulo 97

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 is_on_the_network(peppol_id: str) -> bool:
"""Check registration. peppol_id must be in scheme:value form."""
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)

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
# - 9917 (VAT) - VAT number scheme

# Always check which scheme the receiver is registered with

Scheme Selection Guideโ€‹

When should you use which scheme?

CountryB2BB2GRecommended
๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands010601900106 (KVK)
๐Ÿ‡ฉ๐Ÿ‡ช Germany020402090204 (VAT)
๐Ÿ‡ง๐Ÿ‡ช Belgium020802080208 (CBE)
๐Ÿ‡ซ๐Ÿ‡ท France992500099925 (SIRET)
๐Ÿ‡ฎ๐Ÿ‡น Italy021002110210 (Codice Fiscale)
๐Ÿ‡ณ๐Ÿ‡ด Norway019201920192 (Org.nr)
๐Ÿ‡ธ๐Ÿ‡ช 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โ€‹