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
| Country | Preferred Scheme | Format |
|---|---|---|
| 🇳🇱 Netherlands | 0106 | 8 digits (KVK) |
| 🇧🇪 Belgium | 0208 | 10 digits (CBE) |
| 🇩🇪 Germany | 0204 | DE + 9 digits (VAT) |
| 🇫🇷 France | 9925 | 14 digits (SIRET) |
| 🇮🇹 Italy | 0210 | 11-16 chars (Codice Fiscale) |
| 🇳🇴 Norway | 0192 | 9 digits (Org.nr) |
| 🇸🇪 Sweden | 0007 | 10 digits (Org.nr) |
| 🇸🇬 Singapore | 0195 | UEN 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
| Status | Description |
|---|---|
pending | Registration in progress |
active | Fully registered and active |
suspended | Temporarily disabled |
deregistered | Removed from Peppol network |
| SMP Status | Description |
|---|---|
pending | SMP registration in progress |
registered | Visible on Peppol network |
failed | SMP registration failed |
deregistered | Removed 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