Skip to main content

Participant Lookup

Before sending a document, verify that the receiver is registered on the Peppol network and can receive your document type.

Quick Lookup

import requests

def lookup_participant(scheme: str, identifier: str) -> dict:
"""Look up a participant on the Peppol network."""
response = requests.get(
"https://app.goroute.ai/peppol-api/api/v1/participants/lookup",
params={
"scheme": scheme,
"identifier": identifier
},
headers={"X-API-Key": "your_api_key"}
)

if response.status_code == 404:
return None

response.raise_for_status()
return response.json()


# Look up a Dutch company
result = lookup_participant("0106", "12345678")

if result:
print(f"Found: {result['name']}")
print(f"Country: {result['country']}")
print(f"Document types: {len(result['capabilities'])}")
else:
print("Participant not found on Peppol network")

Lookup Response

{
"found": true,
"participant": {
"scheme": "0106",
"identifier": "12345678",
"name": "Example Company BV",
"country": "NL",
"access_point": {
"name": "GoRoute",
"peppol_id": "POP000991",
"endpoint": "https://ap.goroute.ai/as4"
},
"capabilities": [
{
"document_type": "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2",
"document_type_name": "Invoice",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0",
"process_name": "Peppol BIS Billing 3.0"
},
{
"document_type": "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2",
"document_type_name": "Credit Note",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0",
"process_name": "Peppol BIS Billing 3.0"
}
],
"registered_since": "2023-06-15T00:00:00Z",
"last_updated": "2024-01-10T12:00:00Z"
},
"lookup_source": "smp",
"lookup_time_ms": 245
}

Check Document Type Support

Verify the receiver can accept your document type before sending:

def can_receive(scheme: str, identifier: str, document_type: str) -> bool:
"""Check if participant can receive a specific document type."""
result = lookup_participant(scheme, identifier)

if not result or not result.get("found"):
return False

capabilities = result["participant"]["capabilities"]
return any(
cap["document_type_name"] == document_type or
document_type in cap["document_type"]
for cap in capabilities
)


# Check before sending an invoice
if can_receive("0106", "12345678", "Invoice"):
print("✅ Receiver can accept invoices")
send_invoice(invoice)
else:
print("❌ Receiver cannot accept invoices")

Batch Lookup

Look up multiple participants at once:

def batch_lookup(participants: list[dict]) -> dict:
"""Look up multiple participants in one request."""
response = requests.post(
"https://app.goroute.ai/peppol-api/api/v1/participants/lookup/batch",
headers={
"X-API-Key": "your_api_key",
"Content-Type": "application/json"
},
json={
"participants": participants
}
)
return response.json()


# Look up multiple receivers
results = batch_lookup([
{"scheme": "0106", "identifier": "12345678"},
{"scheme": "0106", "identifier": "87654321"},
{"scheme": "0208", "identifier": "0123456789"}
])

for result in results["results"]:
peppol_id = f"{result['scheme']}:{result['identifier']}"
if result["found"]:
print(f"✅ {peppol_id}: {result['name']}")
else:
print(f"❌ {peppol_id}: Not found")

Search by Name

Search for participants by company name:

def search_participants(query: str, country: str = None) -> list:
"""Search for participants by name."""
params = {"q": query}
if country:
params["country"] = country

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


# Search for a company
results = search_participants("Philips", country="NL")

for participant in results["items"]:
print(f"{participant['name']}: {participant['scheme']}:{participant['identifier']}")

Peppol Directory Integration

GoRoute also queries the official Peppol Directory:

def search_directory(query: str) -> list:
"""Search the Peppol Directory."""
response = requests.get(
"https://app.goroute.ai/peppol-api/api/v1/directory/search",
params={"q": query},
headers={"X-API-Key": "your_api_key"}
)
return response.json()


# Search globally
results = search_directory("IKEA")

for entry in results["items"]:
print(f"{entry['name']} ({entry['country']})")
print(f" Peppol ID: {entry['participant_id']}")
print(f" Access Point: {entry['access_point']}")

Caching Lookups

Cache lookup results to reduce API calls:

import redis
import json
from datetime import timedelta

class ParticipantCache:
def __init__(self, redis_client: redis.Redis, ttl: int = 3600):
self.redis = redis_client
self.ttl = ttl # Cache for 1 hour

def lookup(self, scheme: str, identifier: str) -> dict:
"""Look up participant with caching."""
cache_key = f"peppol:participant:{scheme}:{identifier}"

# Check cache
cached = self.redis.get(cache_key)
if cached:
return json.loads(cached)

# Query API
result = lookup_participant(scheme, identifier)

# Cache result
if result:
self.redis.setex(
cache_key,
self.ttl,
json.dumps(result)
)

return result

def invalidate(self, scheme: str, identifier: str):
"""Invalidate cached participant data."""
cache_key = f"peppol:participant:{scheme}:{identifier}"
self.redis.delete(cache_key)


# Usage
cache = ParticipantCache(redis.Redis())
participant = cache.lookup("0106", "12345678")

Validation Before Sending

Integrate lookup into your sending workflow:

class InvoiceSender:
def __init__(self, api_key: str):
self.api_key = api_key

def send(self, invoice_xml: str) -> dict:
"""Send invoice with pre-flight validation."""

# Extract receiver from invoice
receiver = self.extract_receiver(invoice_xml)

# Look up receiver
lookup = lookup_participant(receiver["scheme"], receiver["identifier"])

if not lookup or not lookup.get("found"):
raise ValueError(
f"Receiver {receiver['scheme']}:{receiver['identifier']} "
"is not registered on Peppol network"
)

# Check document type support
if not self.supports_invoice(lookup["participant"]["capabilities"]):
raise ValueError(
f"Receiver does not accept invoices. "
f"Supported: {[c['document_type_name'] for c in lookup['participant']['capabilities']]}"
)

# Proceed with sending
return self.do_send(invoice_xml)

def supports_invoice(self, capabilities: list) -> bool:
return any(
"Invoice" in cap.get("document_type_name", "") or
"Invoice-2" in cap.get("document_type", "")
for cap in capabilities
)

Error Handling

def safe_lookup(scheme: str, identifier: str) -> dict:
"""Look up with comprehensive error handling."""
try:
result = lookup_participant(scheme, identifier)

if result is None:
return {
"found": False,
"reason": "not_registered",
"message": "Participant is not registered on Peppol network"
}

return result

except requests.HTTPError as e:
if e.response.status_code == 400:
return {
"found": False,
"reason": "invalid_identifier",
"message": "Invalid Peppol identifier format"
}
raise

except requests.Timeout:
return {
"found": False,
"reason": "timeout",
"message": "Lookup timed out, please retry"
}

Lookup Performance

SourceTypical Response Time
GoRoute Cache< 50ms
SMP Query100-500ms
Peppol Directory200-1000ms

Tips for optimal performance:

  1. Use batch lookups when checking multiple receivers
  2. Implement caching for frequently queried participants
  3. Pre-validate receivers during business process (not at send time)

Next Steps