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
| Source | Typical Response Time |
|---|---|
| GoRoute Cache | < 50ms |
| SMP Query | 100-500ms |
| Peppol Directory | 200-1000ms |
Tips for optimal performance:
- Use batch lookups when checking multiple receivers
- Implement caching for frequently queried participants
- Pre-validate receivers during business process (not at send time)