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 number0204:DE123456789โ German VAT number9925:0123456789โ French SIRET number
Common Identifier Schemesโ
European Schemesโ
| Scheme | Country | Name | Format Example |
|---|---|---|---|
0007 | ๐ธ๐ช Sweden | Organisationsnummer | 1234567890 |
0088 | ๐ Global | EAN/GLN | 1234567890123 |
0096 | ๐ฉ๐ฐ Denmark | CVR | 12345678 |
0106 | ๐ณ๐ฑ Netherlands | KVK | 12345678 |
0130 | ๐ช๐บ EU | Directorates-General | DIGIT.B.3 |
0184 | ๐ฉ๐ฐ Denmark | CVR (alternative) | DK12345678 |
0190 | ๐ณ๐ฑ Netherlands | OIN | 00000001234567890000 |
0192 | ๐ณ๐ด Norway | Organisasjonsnummer | 123456789 |
0195 | ๐ธ๐ฌ Singapore | UEN | S12345678A |
0196 | ๐ฎ๐ธ Iceland | Kennitala | 1234567890 |
0198 | ๐ฉ๐ฐ Denmark | ERSTORG | 12345678 |
0200 | ๐ฑ๐น Lithuania | Company Code | 123456789 |
0201 | ๐ฑ๐น Lithuania | VAT | LT123456789 |
0204 | ๐ฉ๐ช Germany | VAT | DE123456789 |
0208 | ๐ง๐ช Belgium | Enterprise Number | 0123456789 |
0209 | ๐ฉ๐ช Germany | Leitweg-ID | 991-12345-67 |
0210 | ๐ฎ๐น Italy | Codice Fiscale | 12345678901 |
0211 | ๐ฎ๐น Italy | IPA Code | ABC123 |
0213 | ๐ช๐ช Estonia | Registry Code | 12345678 |
0215 | ๐ซ๐ฎ Finland | Y-tunnus | 1234567-8 |
0216 | ๐ซ๐ฎ Finland | OVT | 003712345678 |
0221 | ๐ฏ๐ต Japan | Corporate Number | 1234567890123 |
0230 | ๐ฒ๐พ Malaysia | BRN | 202301012345 |
VAT-based Schemesโ
| Scheme | Country | Format |
|---|---|---|
9906 | ๐ฎ๐น Italy | IT + 11 digits |
9914 | ๐ฆ๐น Austria | ATU + 8 digits |
9917 | ๐ณ๐ฑ Netherlands | NL + 9 digits + B + 2 digits |
9918 | ๐ช๐ช Estonia | EE + 9 digits |
9919 | ๐ช๐ธ Spain | ESX + 8 digits |
9920 | ๐ฆ๐น Austria | AT + 9 digits |
9922 | ๐ฆ๐ฉ Andorra | AD + 8 digits |
9923 | ๐ฆ๐ฑ Albania | AL + 10 digits |
9924 | ๐ง๐ฆ Bosnia | BA + 12 digits |
9925 | ๐ซ๐ท France | FR + 11 digits (SIRET) |
9926 | ๐ญ๐บ Hungary | HU + 8 digits |
9930 | ๐ฎ๐น Italy | IT + Partita IVA |
9955 | ๐ธ๐ช Sweden | SE + 12 digits |
9956 | ๐ง๐ช Belgium | BE + 10 digits |
9957 | ๐ซ๐ท France | FR + 11 digits |
9959 | ๐ช๐บ EU | Employer ID Number |
Validating Identifiersโ
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"])
scheme:value formThe 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:
| Scheme | Length | Format | Check Digit |
|---|---|---|---|
0106 | 8 | Numeric only | No |
0192 | 9 | Numeric only | Modulo 11 |
0204 | 11 | DE + 9 digits | Yes |
0208 | 10 | Numeric only | Modulo 97 |
Common Validation Errorsโ
- Including country prefix when not required (e.g.,
NLin KVK numbers) - Omitting country prefix when required (e.g.,
DEin 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?
| Country | B2B | B2G | Recommended |
|---|---|---|---|
| ๐ณ๐ฑ Netherlands | 0106 | 0190 | 0106 (KVK) |
| ๐ฉ๐ช Germany | 0204 | 0209 | 0204 (VAT) |
| ๐ง๐ช Belgium | 0208 | 0208 | 0208 (CBE) |
| ๐ซ๐ท France | 9925 | 0009 | 9925 (SIRET) |
| ๐ฎ๐น Italy | 0210 | 0211 | 0210 (Codice Fiscale) |
| ๐ณ๐ด Norway | 0192 | 0192 | 0192 (Org.nr) |
| ๐ธ๐ช Sweden | 0007 | 0007 | 0007 (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:
| Name | Type | Required | Description |
|---|---|---|---|
peppol_id | string | Yes (or identifier) | The full identifier in scheme:value form, e.g. 0106:12345678 |
identifier | string | Accepted as an alias for peppol_id | Same 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.