Create a Sender
Before you can send invoices through the Peppol network, you need to establish your organization as a "sender" (also known as a Legal Entity). This guide walks you through the complete setup process.
Overviewโ
A sender in GoRoute represents your organization or your client's organization that will send e-invoices. Each sender has:
- Peppol Identifier - A unique ID on the Peppol network (e.g.,
0088:1234567890123) - Organization Details - Legal name, address, VAT number
- Document Capabilities - Types of documents you can send
- SMP Registration - Entry in the Service Metadata Publisher
Step 1: Gather Required Informationโ
Before creating a sender, collect this information:
| Field | Description | Example |
|---|---|---|
| Legal Name | Official registered company name | Acme Corporation B.V. |
| VAT Number | Tax identification number | NL123456789B01 |
| Peppol Scheme | Identifier scheme for your country | 0106 (NL KvK) |
| Peppol ID | Your identifier in the scheme | 12345678 |
| Country | ISO 3166-1 alpha-2 code | NL |
| Address | Registered business address | Herengracht 123, Amsterdam |
Common Peppol Identifier Schemesโ
| Country | Scheme Code | Description |
|---|---|---|
| Netherlands | 0106 | KvK (Chamber of Commerce) |
| Belgium | 0208 | Enterprise Number |
| Germany | 9930 | Leitweg-ID |
| France | 0009 | SIRET |
| Italy | 0211 | Codice Fiscale |
| Global | 0088 | GLN (EAN Location Number) |
See Identifiers for the complete list.
Step 2: Your Organization Already Existsโ
Until this revision, Steps 2 to 5 told you to call /peppol-api/organizations,
/peppol-api/participants, /peppol-api/participants/lookup,
/peppol-api/documents/send and /peppol-api/participants/{scheme:value}. None of those
paths has ever existed โ they omit the /api/v1 part of the address, and two of them
describe operations the API does not offer at all. Code written against them has never
worked. The correct calls are below.
There is no customer-facing call that creates an organization. GoRoute creates it for you when your account request is approved, and your API key identifies it on every request after that. You never pass an organization ID.
If you do not have an organization yet, that is an onboarding step rather than an API step โ see Client Onboarding, which covers the account request, the welcome email and where to find your API key.
The organization records your legal name, country and VAT number. To read them, and to
change your contact and address details, use Company Settings in the dashboard at
app.goroute.ai, or GET and PATCH /api/v1/settings/organization โ
see Organizations & Multi-Tenancy. The country is
required when the organization is created, as an ISO 3166-1 alpha-2 code such as OM, and
PATCH /api/v1/settings/organization does not change it โ see
Organization country.
Step 3: Create the Participant, Then Publish Itโ
Becoming a sender is two calls, not one, and it is worth understanding why:
POST /api/v1/participantsstores the participant record in GoRoute.POST /api/v1/participants/{participant_id}/registerpublishes that record to the Peppol SMP and SML, which is what makes you reachable on the network.
Creating the record does not publish it. A participant that is created but never registered is invisible to the rest of the network.
3a. Create the participant recordโ
curl -X POST https://app.goroute.ai/peppol-api/api/v1/participants \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"scheme": "0106",
"identifier": "12345678",
"display_name": "Acme Corporation B.V.",
"country": "NL",
"contact_email": "invoices@acme.nl",
"document_types": [
"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice"
]
}'
There is no org_id field: your API key already says which organization this belongs to.
Response โ 201 Created (abbreviated; the full record is on
Participant Registration):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"scheme": "0106",
"identifier": "12345678",
"peppol_id": "0106:12345678",
"display_name": "Acme Corporation B.V.",
"country": "NL",
"status": "active",
"smp_registered": false,
"sml_registered": false,
"document_types": [
"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice"
]
}
Note what this response is not. There is no "status": "registered", no smp_url and
no registered_at. smp_registered and sml_registered are booleans and both are false
until step 3b succeeds.
3b. Publish it to the SMP and SMLโ
{participant_id} is the id from the response above โ a UUID such as
550e8400-e29b-41d4-a716-446655440000. It is not 0106:12345678. Passing the
scheme:value identifier fails; every route under /api/v1/participants/{participant_id}
parses this segment as a UUID.
curl -X POST https://app.goroute.ai/peppol-api/api/v1/participants/550e8400-e29b-41d4-a716-446655440000/register \
-H "X-API-Key: your-api-key"
The call takes no request body. Document types are chosen for you from the participant's country: AU and NZ participants get PINT A-NZ, everyone else gets BIS Billing 3.0.
Both calls need the participants:manage permission on your API key. Publishing an
identifier beyond what your plan includes needs the SMP subscription and otherwise answers
402 SUBSCRIPTION_REQUIRED. Participant Registration
covers the options, the full response and the failure cases.
Step 4: Verify Registrationโ
Confirm the sender is visible on the Peppol network. This asks the network, not our database, so it is the honest check that step 3b worked:
curl -G https://app.goroute.ai/peppol-api/api/v1/participants/lookup \
--data-urlencode "peppol_id=0106:12345678" \
-H "X-API-Key: your-api-key"
The query parameter is peppol_id, in scheme:value form. (identifier is accepted as an
older alias for the same thing.) Omitting the colon returns 400.
Response:
{
"found": true,
"participant_id": "0106:12345678",
"name": "Acme Corporation B.V.",
"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
}
A participant that is not on the network returns 200 with "found": false and a message,
not a 404. capabilities is a flat list of document type identifiers. This endpoint needs
the participants:read permission. GET /api/v1/participants/lookup is the address; see
Participant Registration for interpreting the
result.
Step 5: Send Your First Invoiceโ
Now you can send invoices from this sender. Sending is a JSON call โ the UBL document travels as a string inside the JSON body, and you do not POST raw XML:
curl -X POST https://app.goroute.ai/peppol-api/api/v1/documents \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"receiver_scheme": "9959",
"receiver_id": "987654321",
"document": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Invoice ...>"
}'
A successful send answers 202 Accepted with a transaction_id and a status. The address
is POST /api/v1/documents; there is no documents/send path and there never has been.
Sending an Invoice covers the full request, the
response and how to track what happens next.
The AccountingSupplierParty in your invoice must match the registered sender:
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="0106">12345678</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>Acme Corporation B.V.</cbc:Name>
</cac:PartyName>
<!-- ... -->
</cac:Party>
</cac:AccountingSupplierParty>
Multi-Tenant Sendersโ
If you are an integrator sending on behalf of multiple clients, each client is a separate organization with its own API key. You cannot create organizations yourself, and one key can never act for two of them โ see Organizations & Multi-Tenancy. What you automate is the participant set-up, once per client, using that client's key.
import requests
BASE_URL = "https://app.goroute.ai/peppol-api/api/v1"
UBL_INVOICE = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice"
def set_up_sender(api_key: str, company) -> dict:
"""Create the participant for one client, then publish it to the SMP."""
headers = {"X-API-Key": api_key, "Content-Type": "application/json"}
# 1. Create the record. The key decides the organization; there is no org_id.
created = requests.post(
f"{BASE_URL}/participants",
headers=headers,
json={
"scheme": company.peppol_scheme,
"identifier": company.peppol_id,
"display_name": company.legal_name,
"country": company.country,
"document_types": [UBL_INVOICE],
},
)
created.raise_for_status() # 201 Created
participant = created.json()
# 2. Publish it. The path parameter is the record's UUID, not scheme:value.
published = requests.post(
f"{BASE_URL}/participants/{participant['id']}/register",
headers={"X-API-Key": api_key},
)
published.raise_for_status()
return participant
for company in client_companies:
participant = set_up_sender(client_api_keys[company.id], company)
print(f"Registered {company.legal_name} as {participant['peppol_id']}")
Sender Capabilitiesโ
Supported Document Typesโ
By default, senders are registered for:
- Invoice (BIS Billing 3.0)
- Credit Note (BIS Billing 3.0)
You can add additional capabilities:
- Order
- Order Response
- Dispatch Advice
- Message Level Response
Adding Document Typesโ
Update the participant record with PATCH /api/v1/participants/{participant_id}. As in
step 3b, the path parameter is the record's UUID, and the list holds full document type
identifiers rather than short names:
curl -X PATCH https://app.goroute.ai/peppol-api/api/v1/participants/550e8400-e29b-41d4-a716-446655440000 \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"document_types": [
"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice",
"urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2::CreditNote",
"urn:oasis:names:specification:ubl:schema:xsd:Order-2::Order"
]
}'
PATCH changes what GoRoute stores. It does not change what the SMP advertises, so senders
looking you up on the network still see the old set. Call
POST /api/v1/participants/{participant_id}/register again afterwards to republish the
service metadata.
Troubleshootingโ
"Identifier already registered"โ
Problem: The Peppol identifier is registered with another Access Point.
Solution:
- Contact the current AP to initiate a migration
- See Participant Migration
"Invalid identifier format"โ
Problem: The identifier doesn't match the scheme's requirements.
Solution:
- Check the scheme's format requirements
- Remove any spaces or special characters
- Ensure proper length (e.g., GLN must be exactly 13 digits)
"VAT number validation failed"โ
Problem: The VAT number couldn't be verified.
Solution:
- Verify the VAT number with VIES
- Ensure country prefix matches country field
- Check for typos
Best Practicesโ
- Use production identifiers in production - Don't reuse test identifiers
- Verify before sending - Always confirm registration before first send
- Keep information updated - Update organization details when they change
- Test with sandbox first - Validate your integration before going live