API Documentation
Technical guide for integrating your software with Simple Peppol Gateway
Table of Contents
Authentication
All API requests require authentication using a Bearer token. You'll receive an API token when your account is created.
Authorization: Bearer SBG_your_api_token_here
API tokens are prefixed with SBG_ and should be kept secure.
Never commit tokens to version control.
Security Note: Tokens are hashed using SHA-256 with your account's salt. Store tokens securely and rotate them periodically.
Base URL
The base URL is the same for every customer. The environment is selected entirely by the path — the token is used as-is in both.
- Production:
https://peppol-api.simplebill.pro/api/v1— sends to the live Peppol network. - Sandbox:
https://peppol-api.simplebill.pro/api-sandbox/v1— a safe rehearsal that uses the same token, requests and responses as production, but never touches the live network.
How sandbox works. A document you POST to /api-sandbox/v1/documents is validated against the Peppol rules exactly as in production. Its delivery lifecycle is then simulated: the document moves queued → processing → sent, and you receive a simulated inbound webhook so you can exercise your receiving code. Nothing is sent onto the live Peppol network, and sandbox activity is not billed. When your integration succeeds in sandbox, switch the base URL to /api/v1 and use your production token — the behaviour is identical.
Sending Documents
Send Peppol documents (invoices, credit notes, orders) via the POST /api/v1/documents endpoint.
Request
POST /api/v1/documents
Content-Type: application/json
Authorization: Bearer SBG_your_api_token_here
Idempotency-Key: your-unique-idempotency-key (optional)
{
"sender_peppol_id": "0199:pixeline",
"receiver_peppol_id": "0199:triaxis",
"document_type_id": "urn:fdc:peppol.eu:2017:poacc:billing:3:ver2.0",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0",
"payload_format": "ubl",
"payload_xml": "<Invoice xmlns='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'>...</Invoice>",
"client_reference": "simplebill-invoice-1234"
}
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
sender_peppol_id |
string | Yes | Your registered Peppol participant ID (e.g., "0199:pixeline"). View or remove your registered senders in the portal at /portal/senders; contact support to add a sender. |
receiver_peppol_id |
string | Yes | Recipient's Peppol participant ID |
document_type_id |
string | Yes | Peppol document type URN (e.g., invoice, credit note) |
process_id |
string | Yes | Peppol process URN |
payload_format |
string | Yes | Either "ubl" or "sbdh" |
payload_xml |
string | Yes | The UBL XML document (max 10 MB) |
client_reference |
string | No | Your unique reference for idempotency (prevents duplicate sends) |
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued"
}
Checking Document Status
Poll the document status using GET /api/v1/documents/{id}:
GET /api/v1/documents/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer SBG_your_api_token_here
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "sent",
"direction": "outbound",
"sender_peppol_id": "0199:pixeline",
"receiver_peppol_id": "0199:triaxis",
"document_type_id": "urn:fdc:peppol.eu:2017:poacc:billing:3:ver2.0",
"sent_at": "2025-11-29T10:30:00Z"
}
Document statuses: queued,
processing,
sent,
delivered,
failed
Receiving Documents
You can receive inbound Peppol documents in two ways: via webhooks (recommended) or by polling the API.
Polling Method
List inbound documents using GET /api/v1/inbox:
GET /api/v1/inbox?page=1&per_page=50
Authorization: Bearer SBG_your_api_token_here
Response:
{
"data": [
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"status": "delivered",
"sender_peppol_id": "0199:triaxis",
"receiver_peppol_id": "0199:pixeline",
"document_type_id": "urn:fdc:peppol.eu:2017:poacc:billing:3:ver2.0",
"received_at": "2025-11-29T10:30:00Z"
}
],
"current_page": 1,
"per_page": 50,
"total": 1
}
Retrieving Document Payload
Get the full XML payload using GET /api/v1/inbox/{id}:
GET /api/v1/inbox/660e8400-e29b-41d4-a716-446655440000
Authorization: Bearer SBG_your_api_token_here
Response:
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"payload_xml": "<StandardBusinessDocument>...</StandardBusinessDocument>",
...
}
Webhooks
Configure webhooks to receive real-time notifications when documents are received. Webhooks are sent via HTTP POST to your configured endpoint.
Configure your webhook endpoint in the portal at
/portal/webhooks,
separately for each environment (sandbox and production). Endpoints must be
HTTPS. Use the Send test event button to verify your
endpoint — it delivers a signed payload with event
webhook.test that your handler
can safely ignore. Failed deliveries are retried with exponential backoff.
Webhook Payload
POST https://your-endpoint.com/webhooks/peppol
X-SimpleBill-Event: peppol.inbound_document.received
X-SimpleBill-Signature: sha256=abc123...
Content-Type: application/json
{
"event": "peppol.inbound_document.received",
"id": "660e8400-e29b-41d4-a716-446655440000",
"environment": "production",
"sender_peppol_id": "0199:triaxis",
"receiver_peppol_id": "0199:pixeline",
"document_type_id": "urn:fdc:peppol.eu:2017:poacc:billing:3:ver2.0",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0",
"received_at": "2025-11-29T10:30:00Z",
"payload_xml": "<StandardBusinessDocument>...</StandardBusinessDocument>"
}
Every payload carries an environment field —
production for live documents and
sandbox for the simulated callbacks you receive while testing —
so your handler can tell them apart.
Webhook Signature Verification
Verify webhook authenticity by checking the X-SimpleBill-Signature header.
The signature is computed as HMAC-SHA256(request_body, webhook_secret).
Important: Always verify webhook signatures to ensure requests are from Simple Peppol Gateway.
Sender Registration Status
View your registered senders and their registration status using GET /api/v1/senders.
This endpoint returns the senders associated with your account for the selected environment.
Request
GET /api/v1/senders
Authorization: Bearer SBG_your_api_token_here
# For sandbox (simulated lifecycle):
GET /api-sandbox/v1/senders
Authorization: Bearer SBG_your_api_token_here
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"registration_reference": "reg_550e8400-e29b-41d4-a716-446655440000",
"participant_id": "0208:0123456789",
"environment": "production",
"status": "active"
},
{
"registration_reference": "reg_660e8400-e29b-41d4-a716-446655440001",
"participant_id": "0208:9876543210",
"environment": "production",
"status": "revoked"
}
]
}
Sender Status Values
| Status | Description |
|---|---|
active |
The sender is registered and can send and receive Peppol documents. |
revoked |
The sender registration has been revoked and can no longer send documents. Receiving may still be possible. |
pending |
Reserved for the upcoming partner-registration flow. Not currently used. |
rejected |
Reserved for the upcoming partner-registration flow. Not currently used. |
Webhook Event: Sender Registration Status Changed
When a sender's registration status changes, you receive a webhook event if you have configured a webhook endpoint.
The event type is peppol.sender_registration.status_changed.
POST https://your-endpoint.com/webhooks/peppol
X-SimpleBill-Event: peppol.sender_registration.status_changed
X-SimpleBill-Signature: sha256=abc123...
Content-Type: application/json
{
"event": "peppol.sender_registration.status_changed",
"registration_reference": "reg_550e8400-e29b-41d4-a716-446655440000",
"participant_id": "0208:0123456789",
"environment": "production",
"status": "revoked",
"previous_status": "active",
"changed_at": "2025-11-29T10:30:00Z"
}
Usage Tracking
Monitor your API usage for the current billing period:
GET /api/v1/usage/current
Authorization: Bearer SBG_your_api_token_here
Response:
{
"period_start": "2025-11-01",
"period_end": "2025-11-30",
"outbound_count": 123,
"inbound_count": 45,
"outbound_bytes": 1234567,
"inbound_bytes": 7654321
}
Service Status
The current health of the gateway and of the Peppol network is published at
https://peppol-api.simplebill.pro/status,
with a machine-readable version at
GET /status/api/v2/status.json.
Use it to drive your own dashboards, or to decide whether a failed request is worth retrying.
No authentication. This is the only endpoint that takes no API token.
Do not send an Authorization header, and do not treat it as account-specific:
it reports platform-wide status, identical for every caller.
Request
GET /status/api/v2/status.json
Response
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: public, max-age=60
{
"page": {
"id": "simplebill-peppol",
"name": "SimplePeppol Status",
"url": "https://peppol-api.simplebill.pro/status",
"updated_at": "2026-08-18T11:42:04+02:00"
},
"status": {
"indicator": "none",
"description": "All systems operational"
}
}
Status Indicators
Branch on status.indicator, not on
status.description — the description is
human-facing wording and may change.
| Indicator | Meaning |
|---|---|
none |
Everything is operational. Normal processing. |
minor |
Degraded performance or a partial outage. Requests generally succeed but may be slower, and delivery can lag. Keep sending; back off on retries. |
major |
A major outage affecting at least one component. Queue your documents and retry later rather than looping on failures. |
maintenance |
Planned maintenance is in progress. Announced in advance on the status page. |
Polling
Our probes run every two minutes and the response is cached for 60 seconds, so polling more than once a minute returns the same payload and gains you nothing. Once a minute is ample.
page.updated_at is when the underlying checks
last ran, and is null before the first check of a
new deployment. If it is null or well in the past, treat the status as unknown rather than healthy:
it means monitoring itself is not reporting.
Example
curl -s https://peppol-api.simplebill.pro/status/api/v2/status.json
# Gate a batch on platform health before sending
indicator=$(curl -s https://peppol-api.simplebill.pro/status/api/v2/status.json \
| python3 -c "import json,sys; print(json.load(sys.stdin)['status']['indicator'])")
if [ "$indicator" = "major" ]; then
echo "SimplePeppol reports a major outage - holding the batch"
exit 1
fi
Monitoring Tools
The payload follows the Statuspage.io status.json
convention, so most uptime dashboards and status aggregators can consume it as-is without a custom parser.
Worth knowing: this endpoint is served by the gateway itself, so a total gateway outage takes the status endpoint down with it. A request that times out is itself meaningful — do not read "no response" as healthy. If you need certainty independent of us, point an external uptime monitor at this URL.
Error Handling
The API uses standard HTTP status codes:
200- Success400- Bad Request (invalid parameters)401- Unauthorized (invalid or missing token)403- Forbidden (insufficient permissions)404- Not Found422- Validation Error429- Too Many Requests (rate limit exceeded)500- Internal Server Error
Error responses include a JSON body with error details:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"message": "The given data was invalid.",
"errors": {
"sender_peppol_id": ["The sender peppol id field is required."]
}
}
Code Examples
cURL
curl -X POST https://peppol-api.simplebill.pro/api/v1/documents \
-H "Authorization: Bearer SBG_your_api_token_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: invoice-1234" \
-d '{
"sender_peppol_id": "0199:pixeline",
"receiver_peppol_id": "0199:triaxis",
"document_type_id": "urn:fdc:peppol.eu:2017:poacc:billing:3:ver2.0",
"process_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0",
"payload_format": "ubl",
"payload_xml": "<Invoice>...</Invoice>",
"client_reference": "invoice-1234"
}'
PHP (Guzzle)
$client = new \GuzzleHttp\Client();
$response = $client->post('https://peppol-api.simplebill.pro/api/v1/documents', [
'headers' => [
'Authorization' => 'Bearer SBG_your_api_token_here',
'Content-Type' => 'application/json',
'Idempotency-Key' => 'invoice-1234',
],
'json' => [
'sender_peppol_id' => '0199:pixeline',
'receiver_peppol_id' => '0199:triaxis',
'document_type_id' => 'urn:fdc:peppol.eu:2017:poacc:billing:3:ver2.0',
'process_id' => 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
'payload_format' => 'ubl',
'payload_xml' => $ublXml,
'client_reference' => 'invoice-1234',
],
]);
$result = json_decode($response->getBody(), true);
Python (requests)
import requests
response = requests.post(
'https://peppol-api.simplebill.pro/api/v1/documents',
headers={
'Authorization': 'Bearer SBG_your_api_token_here',
'Content-Type': 'application/json',
'Idempotency-Key': 'invoice-1234',
},
json={
'sender_peppol_id': '0199:pixeline',
'receiver_peppol_id': '0199:triaxis',
'document_type_id': 'urn:fdc:peppol.eu:2017:poacc:billing:3:ver2.0',
'process_id': 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
'payload_format': 'ubl',
'payload_xml': ubl_xml,
'client_reference': 'invoice-1234',
}
)
result = response.json()
Need Help?
For additional support, questions, or to request API access, please contact us:
Contact Support