Skip to main content

Participant Registration

Register your organization or your customers as Peppol participants to send and receive documents.

Registration Overview

Registration involves three steps:

1. Create Participant → 2. Configure Capabilities → 3. Publish to SMP

Create a Participant

Basic Registration

import requests

def register_participant(
scheme: str,
identifier: str,
name: str,
country: str,
email: str
) -> dict:
"""Register a new Peppol participant."""

response = requests.post(
"https://app.goroute.ai/peppol-api/api/v1/participants",
headers={
"X-API-Key": "your_api_key",
"Content-Type": "application/json"
},
json={
"scheme": scheme,
"identifier": identifier,
"name": name,
"country": country,
"contact_email": email,
"auto_register_smp": True
}
)

response.raise_for_status()
return response.json()


# Register a Dutch company
participant = register_participant(
scheme="0106",
identifier="12345678",
name="My Company BV",
country="NL",
email="invoices@mycompany.nl"
)

print(f"Participant ID: {participant['id']}")
print(f"Peppol ID: {participant['scheme']}:{participant['identifier']}")

Response

{
"id": "part_abc123",
"scheme": "0106",
"identifier": "12345678",
"name": "My Company BV",
"country": "NL",
"contact_email": "invoices@mycompany.nl",
"status": "active",
"smp_status": "registered",
"capabilities": [
{
"document_type": "Invoice",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
},
{
"document_type": "CreditNote",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
}
],
"created_at": "2024-01-15T10:30:00Z",
"registered_at": "2024-01-15T10:30:45Z"
}

Registration Options

Full Registration Request

participant_data = {
# Required fields
"scheme": "0106",
"identifier": "12345678",
"name": "My Company BV",
"country": "NL",

# Contact information
"contact_email": "invoices@mycompany.nl",
"contact_phone": "+31 20 123 4567",
"technical_contact_email": "it@mycompany.nl",

# Address
"address": {
"street": "Main Street 123",
"city": "Amsterdam",
"postal_code": "1012AB",
"country": "NL"
},

# Registration options
"auto_register_smp": True,
"environment": "production", # or "test"

# Document capabilities
"capabilities": [
{
"document_type": "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
},
{
"document_type": "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
}
],

# Webhook for incoming documents
"webhook_url": "https://your-app.com/webhooks/peppol",

# Metadata
"metadata": {
"internal_id": "CUST-12345",
"erp_code": "NL-001"
}
}

Choosing the Right Identifier

By Country

CountryPreferred SchemeFormat
🇳🇱 Netherlands01068 digits (KVK)
🇧🇪 Belgium020810 digits (CBE)
🇩🇪 Germany0204DE + 9 digits (VAT)
🇫🇷 France992514 digits (SIRET)
🇮🇹 Italy021011-16 chars (Codice Fiscale)
🇳🇴 Norway01929 digits (Org.nr)
🇸🇪 Sweden000710 digits (Org.nr)
🇸🇬 Singapore0195UEN format

Validation

GoRoute validates identifier formats:

# Validate before registration
validation = requests.post(
"https://app.goroute.ai/peppol-api/api/v1/validate/identifier",
headers={"X-API-Key": "your_api_key"},
json={
"scheme": "0106",
"identifier": "12345678"
}
)

result = validation.json()
if result["valid"]:
print(f"✅ Valid {result['scheme_name']} number")
else:
print(f"❌ Invalid: {result['error']}")

Configure Capabilities

Add Document Types

def add_capability(participant_id: str, document_type: str, process_id: str):
"""Add a document capability to a participant."""
response = requests.post(
f"https://app.goroute.ai/peppol-api/api/v1/participants/{participant_id}/capabilities",
headers={
"X-API-Key": "your_api_key",
"Content-Type": "application/json"
},
json={
"document_type": document_type,
"process_id": process_id
}
)
return response.json()


# Add order capability
add_capability(
"part_abc123",
document_type="urn:oasis:names:specification:ubl:schema:xsd:Order-2",
process_id="urn:fdc:peppol.eu:2017:poacc:ordering:01:1.0"
)

Standard Capability Sets

# Billing only (invoices and credit notes)
BILLING_CAPABILITIES = [
{
"document_type": "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
},
{
"document_type": "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
}
]

# Full procurement
FULL_CAPABILITIES = BILLING_CAPABILITIES + [
{
"document_type": "urn:oasis:names:specification:ubl:schema:xsd:Order-2",
"process_id": "urn:fdc:peppol.eu:2017:poacc:ordering:01:1.0"
},
{
"document_type": "urn:oasis:names:specification:ubl:schema:xsd:OrderResponse-2",
"process_id": "urn:fdc:peppol.eu:2017:poacc:ordering:01:1.0"
},
{
"document_type": "urn:oasis:names:specification:ubl:schema:xsd:DespatchAdvice-2",
"process_id": "urn:fdc:peppol.eu:2017:poacc:despatch_advice:01:1.0"
}
]

Multi-Tenant Registration

For platforms managing multiple participants:

class ParticipantManager:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://app.goroute.ai/peppol-api"

def register_customer(
self,
scheme: str,
identifier: str,
name: str,
country: str,
email: str,
internal_id: str
) -> dict:
"""Register a customer as a Peppol participant."""

# Check if already registered
existing = self.find_participant(scheme, identifier)
if existing:
return existing

# Register new participant
response = requests.post(
f"{self.base_url}/api/v1/participants",
headers={
"X-API-Key": self.api_key,
"Content-Type": "application/json"
},
json={
"scheme": scheme,
"identifier": identifier,
"name": name,
"country": country,
"contact_email": email,
"auto_register_smp": True,
"capabilities": BILLING_CAPABILITIES,
"metadata": {
"internal_id": internal_id
}
}
)
response.raise_for_status()
return response.json()

def find_participant(self, scheme: str, identifier: str) -> dict:
"""Find an existing participant."""
response = requests.get(
f"{self.base_url}/api/v1/participants",
params={"scheme": scheme, "identifier": identifier},
headers={"X-API-Key": self.api_key}
)

if response.status_code == 200:
items = response.json().get("items", [])
return items[0] if items else None
return None


# Usage
manager = ParticipantManager("your_api_key")

# Register customers
for customer in customers:
participant = manager.register_customer(
scheme=customer.peppol_scheme,
identifier=customer.peppol_id,
name=customer.company_name,
country=customer.country_code,
email=customer.email,
internal_id=customer.id
)
print(f"Registered: {participant['scheme']}:{participant['identifier']}")

Registration Status

Check Status

def get_participant(participant_id: str) -> dict:
"""Get participant details and status."""
response = requests.get(
f"https://app.goroute.ai/peppol-api/api/v1/participants/{participant_id}",
headers={"X-API-Key": "your_api_key"}
)
return response.json()

participant = get_participant("part_abc123")
print(f"Status: {participant['status']}")
print(f"SMP Status: {participant['smp_status']}")

Status Values

StatusDescription
pendingRegistration in progress
activeFully registered and active
suspendedTemporarily disabled
deregisteredRemoved from Peppol network
SMP StatusDescription
pendingSMP registration in progress
registeredVisible on Peppol network
failedSMP registration failed
deregisteredRemoved from SMP

Error Handling

Common Registration Errors

try:
participant = register_participant(...)
except requests.HTTPError as e:
error = e.response.json()

if error["code"] == "IDENTIFIER_IN_USE":
print("This identifier is already registered with another Access Point")
# Contact the other AP for migration

elif error["code"] == "INVALID_IDENTIFIER":
print(f"Invalid identifier format: {error['message']}")

elif error["code"] == "DUPLICATE_PARTICIPANT":
print("This participant already exists in your organization")
# Return existing participant instead

Next Steps