Skip to main content

SMP Registration

The Service Metadata Publisher (SMP) is the directory that tells the Peppol network where to find you. When you register in the SMP, other participants can discover your capabilities and send you documents.

You do not need the API for this

SMP registration is fully self-serve in the browser: the registration wizard walks through business details, participants, Access Point choice (GoRoute's AP, your own, or another provider's) and document types, and the SMP Dashboard shows live network status, the published business card and document types, certificate expiry, and provides register / deregister / delete with confirmation dialogs. The API below is the machine equivalent of the same flow.

What is SMP?โ€‹

The SMP stores:

  1. Who you are โ€” Your Peppol identifier
  2. What you can receive โ€” Document types and processes
  3. How to reach you โ€” Your Access Point's endpoint
  4. Security info โ€” Certificates for message encryption
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ SMP Record โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Participant: 0106:12345678 โ”‚
โ”‚ โ”œโ”€โ”€ Document: Invoice-2 โ”‚
โ”‚ โ”‚ โ””โ”€โ”€ Process: BIS Billing 3.0 โ”‚
โ”‚ โ”‚ โ””โ”€โ”€ Endpoint: https://ap.goroute.ai/as4 โ”‚
โ”‚ โ””โ”€โ”€ Document: CreditNote-2 โ”‚
โ”‚ โ””โ”€โ”€ Process: BIS Billing 3.0 โ”‚
โ”‚ โ””โ”€โ”€ Endpoint: https://ap.goroute.ai/as4 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

How SMP Lookup Worksโ€‹

When GoRoute sends an invoice to a receiver:

1. Sender provides: 0106:12345678 (receiver ID)
โ”‚
โ–ผ
2. GoRoute queries SML: Where is this participant's SMP?
โ”‚
โ–ผ
3. SML returns: smp-goroute.acc.edelivery.tech (SMP URL)
โ”‚
โ–ผ
4. GoRoute queries SMP: What are this participant's capabilities?
โ”‚
โ–ผ
5. SMP returns:
- Document types: Invoice, Credit Note
- Access Point: https://receiver-ap.example.com/as4
- Certificate: [X.509 certificate]
โ”‚
โ–ผ
6. GoRoute delivers invoice to receiver's Access Point

Registration Processโ€‹

Step 1: Create Participantโ€‹

First, create a participant record in GoRoute:

import requests

participant = {
"scheme": "0106",
"identifier": "12345678",
"name": "My Company BV",
"country": "NL",
"email": "invoices@mycompany.nl"
}

response = requests.post(
"https://app.goroute.ai/peppol-api/api/v1/participants",
headers={
"X-API-Key": "your_api_key",
"Content-Type": "application/json"
},
json=participant
)

result = response.json()
print(f"Participant ID: {result['id']}")

Step 2: Register in SMPโ€‹

POST /api/v1/participants/{participant_id}/register โ€” requires the participants:manage permission.

{participant_id} is a UUID, not a Peppol identifier

The path parameter is the UUID of the GoRoute participant record returned by Step 1 โ€” the id field. It is not the scheme:value Peppol identifier. Passing 0106:12345678 here fails.

Document types: country defaults, or an explicit selection

With no body (or {}), GoRoute registers the participant's country default set:

  • AU and NZ: PINT A-NZ Invoice and Credit Note, published under the peppol-doctype-wildcard scheme only (the PINT A-NZ Billing BIS forbids busdox-docid-qns for this profile), each as two entries โ€” the exact id (โ€ฆ@aunz-1::2.1) and the wildcard id (โ€ฆ@aunz-1*::2.1). This is exactly the shape GoRoute's ATO accreditation testing required.
  • All other countries: Peppol BIS Billing 3.0 Invoice and Credit Note under busdox-docid-qns.

Optionally send {"document_types": [...]} with ids from GET /peppol/doc-types?country=XX (public). That endpoint returns the curated set for the country โ€” one entry per document type with a label and a default flag. Non-default (opt-in) types exist for AU/NZ: EU BIS Billing 3.0, PINT A-NZ Self-Billing, Peppol BIS Ordering 3.0 (Order + Order Response) and Peppol BIS Despatch Advice 3.0. Arbitrary URNs outside the curated set are never published.

An explicit selection is the full desired set โ€” document types you leave out are removed from the SMP (reconciled), which is how "remove a document type" works. A default (bodyless) registration only ever adds.

import requests

response = requests.post(
f"https://app.goroute.ai/peppol-api/api/v1/participants/{participant_id}/register",
headers={"X-API-Key": "your_api_key"},
)

result = response.json()
print(result["participant"], result["service_group"])
print(result["smp_registered"], result["sml_registered"], result["total_doc_types"])

The response reports what the SMP accepted:

FieldMeaning
participantThe scheme:value Peppol identifier
service_groupcreated, already_exists, or error_<status>
document_typesOne entry per document type, each with doc_type_id, scheme and a status of ok or error_<status>
smp_registeredTrue only when the ServiceGroup and every document type succeeded
sml_registeredTrue when the ServiceGroup succeeded โ€” PHOSS creates the SML entry alongside it
total_doc_typesCount of document types now recorded locally
sml_registered and smp_registered move independently

A participant whose ServiceGroup was created but whose document types were rejected exists in the SML and resolves to nothing. A sender addressing it receives a 404 and an AS4 TRANSPORT_ERROR. If smp_registered is false, the registration is incomplete even though the participant looks present.

Billing gate on fresh publishesโ€‹

In production, registering a participant that is not yet on the network requires the organisation's SMP subscription. Without one the endpoint returns HTTP 402 with error_code: SUBSCRIPTION_REQUIRED and nothing is published. The gate applies only to fresh publishes: re-registering an already-published participant (document-type changes, business-card republish) is maintenance and always works, as do deregistration and deletion โ€” billing never strands a live registration.

The billing surface itself:

EndpointPurpose
GET /api/v1/billing/smpSubscription state for the caller's org, plus whether checkout is available
POST /api/v1/billing/smp/checkout-sessionServer-created Stripe Checkout (USD-fixed); returns a redirect URL. 409 if already subscribed
POST /api/v1/billing/smp/portal-sessionStripe customer portal (cards, invoices, cancellation) for existing subscribers

Stripe is the system of record for payment; GoRoute stores no card data.

Registration errorsโ€‹

Registration returns HTTP 502 with one of these error codes:

error_codeWhen
SMP_REGISTRATION_FAILEDThe PHOSS SMP refused or failed the registration
OMAN_SMP_REGISTRATION_FAILEDAn Oman participant was refused by OTA's SMP wrapper
PARTICIPANT_NOT_FOUNDHTTP 404 โ€” no participant with that UUID in your organisation

The Oman error carries a hint: OTA's SMP verifies with the Fawtara Portal that an accepted ServiceProvider-Taxpayer connection exists before it will register a participant. Accept the taxpayer's connection request on the portal first, then retry within three business days.

Step 3: Verify Registrationโ€‹

GET /api/v1/participants/{participant_id}/smp-status โ€” note the hyphen. smp-status is a single path segment; it is not two segments separated by a slash.

response = requests.get(
f"https://app.goroute.ai/peppol-api/api/v1/participants/{participant_id}/smp-status",
headers={"X-API-Key": "your_api_key"},
)

status = response.json()
print(status["registered"], status["participant"])

For a participant on GoRoute's own PHOSS SMP the response carries registered, participant, the service_group_xml when the participant is present, and the locally recorded local_smp_registered, local_sml_registered and local_document_types.

Oman participants take a different path

An Oman participant does not live in GoRoute's PHOSS SMP, and OTA's wrapper is PUT/DELETE only โ€” there is no read route to query. smp-status returns what GoRoute recorded at registration time (registered, participant, rail: "oman-central-smp", smp_registered, sml_registered, document_types) plus a note saying so. Verify authoritatively via the Fawtara Portal (E-Services > SMP Management), SML DNS resolution, or a public SMP GET.

A failed status check returns HTTP 502 with error_code: SMP_CHECK_FAILED.

SMP Record Structureโ€‹

An SMP record contains:

Service Groupโ€‹

<?xml version="1.0" encoding="UTF-8"?>
<ServiceGroup xmlns="http://busdox.org/serviceMetadata/publishing/1.0/">
<ParticipantIdentifier scheme="iso6523-actorid-upis">0106:12345678</ParticipantIdentifier>
<ServiceMetadataReferenceCollection>
<ServiceMetadataReference
href="https://smp.goroute.ai/0106:12345678/services/urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"/>
</ServiceMetadataReferenceCollection>
</ServiceGroup>

Service Metadataโ€‹

<?xml version="1.0" encoding="UTF-8"?>
<ServiceMetadata xmlns="http://busdox.org/serviceMetadata/publishing/1.0/">
<ServiceInformation>
<ParticipantIdentifier scheme="iso6523-actorid-upis">0106:12345678</ParticipantIdentifier>
<DocumentIdentifier scheme="busdox-docid-qns">
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
</DocumentIdentifier>
<ProcessList>
<Process>
<ProcessIdentifier scheme="cenbii-procid-ubl">
urn:fdc:peppol.eu:2017:poacc:billing:01:1.0
</ProcessIdentifier>
<ServiceEndpointList>
<Endpoint transportProfile="peppol-transport-as4-v2_0">
<EndpointURI>https://ap.goroute.ai/as4</EndpointURI>
<RequireBusinessLevelSignature>false</RequireBusinessLevelSignature>
<Certificate>MIIF...</Certificate>
<ServiceDescription>GoRoute Peppol Access Point</ServiceDescription>
<TechnicalContactUrl>https://goroute.ai/#contact</TechnicalContactUrl>
</Endpoint>
</ServiceEndpointList>
</Process>
</ProcessList>
</ServiceInformation>
</ServiceMetadata>

Document Capabilitiesโ€‹

What a participant advertises comes from the curated per-country set โ€” ask the API:

response = requests.get(
"https://app.goroute.ai/peppol-api/peppol/doc-types",
params={"country": "AU"}, # public endpoint, no auth
)
for g in response.json()["doc_types"]:
print("DEFAULT" if g["default"] else "opt-in ", g["label"])

For Australia / New Zealand that prints:

Document typeScheme
defaultPINT A-NZ โ€” Invoicepeppol-doctype-wildcard (exact + * ids)
defaultPINT A-NZ โ€” Credit Notepeppol-doctype-wildcard (exact + * ids)
opt-inPeppol BIS Billing 3.0 โ€” Invoice / Credit Notebusdox-docid-qns
opt-inPINT A-NZ Self-Billing โ€” Invoice / Credit Notepeppol-doctype-wildcard (exact + * ids)
opt-inPeppol BIS Ordering 3.0 โ€” Order / Order Responsebusdox-docid-qns
opt-inPeppol BIS Despatch Advice 3.0busdox-docid-qns

Every other country currently gets Peppol BIS Billing 3.0 Invoice + Credit Note as its default, with no opt-ins. Opt-in types are deliberately never part of a default registration: advertising an Order or Despatch Advice capability is a promise that the participant's Access Point accepts those documents โ€” a choice only the participant can make, in the wizard's checklist or the SMP Dashboard's Manage control.

SMP Managementโ€‹

Changing document capabilitiesโ€‹

Re-register with an explicit selection โ€” the registration endpoint doubles as the capability editor:

requests.post(
f"https://app.goroute.ai/peppol-api/api/v1/participants/{participant_id}/register",
headers={"X-API-Key": "your_api_key", "Content-Type": "application/json"},
json={"document_types": [
# ids from GET /peppol/doc-types?country=XX โ€” the FULL desired set
]},
)

The explicit list is reconciled against what the SMP currently publishes: selected types are added or refreshed, de-selected types are deleted from the SMP. In the browser, the SMP Dashboard's Manage control on each registered participant is the same operation as a checklist. If the country itself is wrong, correct country on the participant record first โ€” it decides which curated set applies.

An earlier revision documented a PUT capability route

No such route ever existed, and there is still no per-capability endpoint. The only lever is the registration reconcile described above, and only ids from the curated country set are ever published.

Deregister from SMPโ€‹

POST /api/v1/participants/{participant_id}/deregister โ€” a POST, not a DELETE, and it requires the participants:manage permission.

response = requests.post(
f"https://app.goroute.ai/peppol-api/api/v1/participants/{participant_id}/deregister",
headers={"X-API-Key": "your_api_key"},
)

result = response.json()
print(result["participant"], result["status"])
statusMeaning
deletedThe ServiceGroup was removed
not_foundIt was already gone โ€” the desired end state, so this counts as success
error_<status>The SMP refused; the participant is still published

Deleting the ServiceGroup cascades to all of its ServiceMetadata. On success the local record is kept but marked unregistered, and its document types are cleared.

A refused deregistration does not change local state

If the SMP refuses the delete, GoRoute deliberately leaves the local flags untouched and the call fails with HTTP 502 and error_code: SMP_DEREGISTRATION_FAILED. Recording a removal that did not happen would mean believing a participant was gone while they remained reachable โ€” and Oman requires deregistration within one working day of a connection ending. Re-check with smp-status rather than assuming.

Deregistration

Deregistering from SMP means other participants can no longer send you documents via Peppol. Only do this if you're migrating to another Access Point or leaving the network.

Delete the participant recordโ€‹

After deregistration, DELETE /api/v1/participants/{participant_id} (HTTP 204) soft-deletes the local record โ€” it disappears from the SMP Dashboard and the wizard. The dashboard offers Delete only for participants that are off the network, so an active registration cannot be orphaned from the UI; the API caller carries that responsibility itself: always deregister first.

Peppol Directory listingโ€‹

Registration makes a participant routable immediately โ€” any sender's SMP lookup finds it. The public Peppol Directory (directory.peppol.eu; test participants: test-directory.peppol.eu) is a separate search index: after each registration or business-card change, GoRoute's SMP announces the participant to the Directory, which then fetches the business card and indexes it asynchronously โ€” typically minutes, up to about an hour. A fresh registration that lookups resolve but Directory search does not yet show is normal, not a fault.

Test vs Productionโ€‹

Test SMPโ€‹

SML: acc.edelivery.tech
SMP: smp-test.goroute.ai

Production SMPโ€‹

SML: edelivery.tech
SMP: smp.goroute.ai
Environment Isolation

Test and production are completely separate networks. A participant registered in test cannot receive documents from production, and vice versa.

Troubleshootingโ€‹

Registration Failedโ€‹

Error: "Participant already registered with another AP"

The identifier is already registered with a different Access Point. The previous provider must deregister before GoRoute can register.

# Contact GoRoute support for migration assistance
# admin@goroute.ai

Lookup Returns Nothingโ€‹

Error: "Participant not found in SMP"

  1. Check if registration completed successfully
  2. Verify you're using the correct environment (test/prod)
  3. Wait 5-10 minutes for DNS propagation

Registered but not in the Peppol Directoryโ€‹

See Peppol Directory listing โ€” Directory indexing is asynchronous. If a participant is still missing after a few hours, contact admin@goroute.ai.

Wrong Document Typesโ€‹

Error: "Receiver does not support document type"

The receiver's SMP registration doesn't include the document type you're trying to send.

# Check the receiver's capabilities first. The lookup takes a single
# peppol_id in scheme:value form, not a separate scheme and identifier.
response = requests.get(
"https://app.goroute.ai/peppol-api/api/v1/participants/lookup",
params={"peppol_id": "0106:12345678"},
headers={"X-API-Key": "your_api_key"}
)

capabilities = response.json()["capabilities"]

Best Practicesโ€‹

  1. Register Early โ€” Complete SMP registration before going live
  2. Test First โ€” Always register in test environment before production
  3. Check What Was Registered โ€” Document types are derived from the participant's country, so confirm the set with smp-status rather than assuming you chose them
  4. Monitor Status โ€” Set up alerts for SMP registration changes
  5. Keep Records โ€” Maintain history of registration changes

API Referenceโ€‹

All paths are relative to https://app.goroute.ai/peppol-api. {participant_id} is the UUID of the GoRoute participant record in every case.

MethodPathPermissionNotes
POST/api/v1/participants/{participant_id}/registerparticipants:manageNo request body. Document types are derived from the participant's country.
POST/api/v1/participants/{participant_id}/deregisterparticipants:managePOST, not DELETE
GET/api/v1/participants/{participant_id}/smp-statusAuthenticatedA single hyphenated path segment

There is no capabilities-editing route. See "Changing document capabilities" above.

Next Stepsโ€‹