API Documentation

Technical guide for integrating your software with Simple Peppol Gateway

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 queuedprocessingsent, 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 - Success
  • 400 - Bad Request (invalid parameters)
  • 401 - Unauthorized (invalid or missing token)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Not Found
  • 422 - Validation Error
  • 429 - 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