Einführung
Die Verteco API ist REST über HTTPS. Request und Response sind JSON (UTF-8). Beginnen Sie mit dem Anlegen eines Kontos, dann eines API-Tokens, und führen Sie den ersten Aufruf innerhalb einer Minute aus. Alle Pfade sind relativ zur unten angegebenen Basis-URL.
Die komplette Testumgebung (Sandbox) finden Sie auf test.peppol.verteco.digital. Sie verhält sich genauso wie die Produktion und bietet zusätzlich Testwerkzeuge (FS-Webhook, Verifizierungsmerkmal (Verifikačný údaj), Löschen einer Firma) und eine simulierte Anbieterauswahl anstelle des Portals der Finanzverwaltung der Slowakischen Republik (Finančná správa, FS).
- Base URL
https://peppol.verteco.digital/api/v1- Version
- v1 (im Pfad). Rückwärtsinkompatible Änderungen erscheinen unter einer neuen Version.
- Protokoll
- nur HTTPS · TLS 1.2+ · TLS A+
- Format
- application/json (UTF-8)
- Auth
- Cookie-Session (Portal) oder Bearer-Token (Server-to-Server)
- Zeitangaben
- ISO-8601 (UTC), z. B. 2026-06-03T09:40:27Z
Zertifizierter AP
Ein echter Peppol Access Point (Seat ID PSK001128), kein Reseller. Wir haben die OpenPeppol-Conformance bestanden (19/19), im Produktivbetrieb.
Valide Belege
Serverseitige Validierung nach EN 16931 + Peppol BIS 3.0 Schematron bei jedem empfangenen UND gesendeten Beleg. Sie können einen Beleg auch vorab prüfen: der öffentliche Validator läuft unter /validator (und als API unter POST /api/v1/public/peppol-validate); Fehler geben wir mit der konkreten Regel zurück (z. B. BR-CO-15).
Multi-tenant
Ein Konto und ein Token für N Firmen, ideal für SaaS, ERP und Buchhalter.
SK TDD automatisch
Die Steuermeldung (corner-5 / TDD) an die Finanzverwaltung erzeugen und senden wir für Sie.
openapi-generator einen typisierten Client in einer beliebigen Sprache (TS, Java, PHP, Python…). Sie deckt sowohl die Portal-API als auch SAPI-SK ab.Schnellstart
Konto und API-Token legen Sie im Portal an (über den Browser). Die Integration läuft danach ausschließlich über den API-Token. Registrierung, Anmeldung und Passwörter muss Ihre App nicht behandeln.
- 1
Legen Sie ein Konto im Portal an
Registrieren Sie sich per E-Mail und bestätigen Sie es über den Link in der E-Mail. - 2
Generieren Sie einen API-Token
Im Portal API tokeny → Vytvoriť (API-Tokens → Erstellen). Der Tokenvpt_…wird nur einmal angezeigt. Bewahren Sie ihn sicher auf. - 3
Führen Sie den ersten Aufruf aus
Verwenden Sie den Token im HeaderAuthorization:javascriptconst res = await fetch('https://peppol.verteco.digital/api/v1/companies', { headers: { Authorization: 'Bearer vpt_8f2a…' }, }); const companies = await res.json();
Testumgebung (Sandbox)
Neben der SAPI-Mock-Sandbox (unten) betreiben wir auch eine vollwertige Testumgebung, eine vollständige Kopie dieses Portals mit getrennten Daten, in der Sie den gesamten Ablauf (Registrierung → Firma → Senden → Empfangen → Benachrichtigungen) end-to-end ausprobieren können, ohne jegliche Auswirkung auf den Produktivbetrieb.
- Registrierung
- ohne Bestätigungs-E-Mail; das Konto ist sofort nutzbar
- Genehmigung der Firma
- automatisch, direkt in der Oberfläche oder über die API; ohne Auswahl des Anbieters im Portal der Finanzverwaltung der Slowakischen Republik (Finančná správa, FS) und ohne Anmeldung im FS-Portal; unmittelbar nach dem Anlegen einer Firma mit USt-IdNr. (IČ DPH) können Sie sowohl senden als auch empfangen
- Registrierung im Netzwerk
- automatisch in das Peppol-Testnetzwerk. Beachten Sie die zwei Ebenen des Identifikators: API und Portal verwenden dasselbe Format wie die Produktion (
peppolParticipantId = 0245:<DIČ-Ziffern>, DIČ = slowakische Steuernummer; Ihr Integrationscode ändert sich nicht); im Test-SMP/SML wird die Firma technisch als9950:SK<DIČ>eingetragen, weil das Schema 0245 einen Verifizierungscode der Finanzverwaltung verlangt, der im Test nicht existiert. In der Produktion wird0245:<DIČ-Ziffern>erst nach der Auswahl des Anbieters im FS-Portal (Anmeldung per eID oder mit den Zugangsdaten) in das produktive Netzwerk registriert - Validierung
- real: Regeln nach EN 16931 + Peppol BIS 3.0, dieselben wie im Produktivbetrieb
- Zustellung
- real über das Peppol-Testnetzwerk (AS4, Testzertifikat, Test-SML/SMP): ein im Testnetzwerk registrierter Empfänger erhält die Rechnung als empfangene Rechnung (inklusive E-Mail, PDF und Webhook); in das produktive Netzwerk geht nichts hinaus
- Zustellnachweis (MLS)
- echter MLS-Zustellnachweis aus dem Testnetzwerk
- E-Mails
- werden real versendet (an die Adressen, die Sie angeben), mit dem Präfix
[TEST] - Preis
- kostenlos, ohne Limits zum Ausprobieren
Dort funktioniert alles wie hier: Portal, REST API, SAPI-SK 1.0, E-Shop-Plugins und Webhooks. Es genügt, in Ihrer Integration die Domain durch test.peppol.verteco.digital zu ersetzen und die in der Testumgebung erstellten Token zu verwenden. Ideal für die Entwicklung der Integration, CI-Tests und die Schulung von Buchhaltern vor dem Produktivstart.
Hinweis: Der Edge-Schutz der Testdomain blockiert die generischen Header User-Agent: Python-urllib und User-Agent: Java/1.8.x (der Standard-User-Agent von HttpsURLConnection in Java 8) mit HTTP 403 "error code: 1010" noch vor unserer API; die Produktivdomain peppol.verteco.digital lässt sie durch. Setzen Sie einen eigenen User-Agent: in Java entweder per JVM-Parameter -Dhttp.agent=meine-app/1.0(ohne Codeänderung; Java hängt "Java/1.8" an, das resultierende "meine-app/1.0 Java/1.8.0_xxx" wird durchgelassen, blockiert wird nur ein Header, der direkt mit "Java/1.8" beginnt) oder auf der Verbindung mit conn.setRequestProperty("User-Agent", "meine-app/1.0"). Gängige Clients (requests, httpx, Java 11+, Apache HttpClient, okhttp, axios, Go, PHP, curl) funktionieren unverändert.
Warum Firmen automatisch genehmigt werden
Die Finanzverwaltung hat keine Testumgebung des VPDS-Portals: Die Auswahl des Anbieters auf vpds.financnasprava.sk läuft nur in der Produktion und wird durch Anmeldung im FS-Portal (eID oder FS-Zugangsdaten) verifiziert, sodass Sie im Produktivbetrieb keine beliebige fremde Firma „auswählen" können. Damit Sie Ihre Integration trotzdem testen können, simulieren wir diesen Schritt in der Testumgebung: Jede Firma, die Sie anlegen, wird automatisch genehmigt (ohne Auswahl bei der FS), sodass Sie Absender und Empfänger anlegen und den gesamten Ablauf durchlaufen können.
Zum Testen der FS-Webhooks (Auswahl des Anbieters) haben wir ein eigenes Gegenstück zum Werkzeug der Finanzverwaltung: test.peppol.verteco.digital/sandbox-nastroje. Dort erzeugen Sie ein gültiges Verifizierungs-Token (Verifizierungsmerkmal, Verifikačný údaj) im Stil der FS und senden einen vollständigen Webhook an Ihren Endpoint, genau so, wie es das FS-Portal bei einer realen Auswahl tut. Zu Testzwecken angelegte Firmen können Sie wiederum deregistrieren (aus dem Portal und aus dem Test-SMP), gerade weil es auf Seiten der Finanzverwaltung keinen Testmodus gibt.
Authentifizierung
Die Integration authentifiziert sich mit einem API-Token im Header Authorization: Bearer vpt_…. Den Token erstellen Sie im Portal (API-Tokens); er hat das Format vpt_ + 40 Hex-Zeichen, wir speichern nur seinen SHA-256-Hash und er hat dieselben Zugriffsrechte wie Ihr Konto. Testen Sie ihn über /auth/me:
curl https://peppol.verteco.digital/api/v1/auth/me -H 'Authorization: Bearer vpt_8f2a…'
# 200 → { "id": "…", "email": "vy(at)firma.sk" } (Token funktioniert)Bei fehlendem oder ungültigem Token gibt die API zurück:
// 401 Unauthorized
{ "error": "unauthorized", "message": "Authentication required" }portal_session, JWT, 7 Tage); bei einer Server-to-Server-Integration benötigen Sie es nicht. Öffentlich ohne Auth sind /ping, /openapi.json und die Endpoints unter /public/* (Prüfung peppol-check, Validator peppol-validate, Details dazu im Abschnitt Öffentliche Werkzeuge, sowie der Dienststatus status); alles andere erfordert eine Session oder einen Bearer-Token.Konventionen & Formate
| Typ | Format | Beispiel |
|---|---|---|
id | UUID (String) | 08dd6c1e-… |
Zeitstempel | ISO-8601 Instant (UTC) | 2026-06-03T09:40:27Z |
issueDate | Datum (YYYY-MM-DD) | 2026-06-03 |
totalAmount | Dezimalzahl | 120.00 |
fehlende Werte | null (Feld ist vorhanden, wird nicht weggelassen) | "dic": null |
Paginierung & Idempotenz
/companies und /companies/{id}/documents unterstützen optional ?page&limit (die Response bleibt ein JSON-Array; die Gesamtzahl finden Sie in den Headern X-Total-Count / X-Total-Pages). Ohne Parameter geben sie das gesamte Array zurück, Belege nach createdAt absteigend sortiert; /tokens gibt immer das gesamte Array zurück. Den Header Idempotency-Key unterstützt die nationale Schnittstelle SAPI-SK auf POST /sapi/document/send; für den programmatischen Versand verwenden Sie genau diese.Fehler
Fehler haben eine einheitliche Form mit einem maschinenlesbaren error-Code. Validierungsfehler ergänzen eine Map fields (erster Fehler pro Feld).
// Business-Fehler
{ "error": "invalid_credentials", "message": "Invalid email or password" }
// Validierungsfehler (400); die Texte in "fields" liefert die API auf Slowakisch
{ "error": "validation_failed", "message": "Some fields are invalid",
"fields": { "ico": "IČO musí byť 8 číslic" } }HTTP-Status
- 200 / 201 / 204
- Erfolg (OK / erstellt / kein Inhalt)
- 400
- ungültige Eingabe (siehe error / fields)
- 401
- fehlende oder ungültige Authentifizierung
- 403
- unzureichende Berechtigung (Rolle)
- 404
- Ressource existiert nicht oder Sie haben keinen Zugriff darauf
- 405
- HTTP-Methode für diesen Pfad nicht unterstützt
- 409
- Konflikt (Handelsregisternummer IČO / E-Mail existiert bereits)
- 415
- nicht unterstützter Content-Type (XML-Endpoints erwarten application/xml)
- 429
- Rate-Limit überschritten (Header RateLimit-* und Retry-After, JSON-Body rate_limited)
error. Eine Antwort ohne JSON-Body (HTML oder Text wie error code: 1010 mit Status 403, oder 502/504 während eines Deployments) kam nicht von der API, sondern von der davor liegenden Infrastruktur (Edge-Schutz, Gateway). Typische Ursache von 403/1010 ist ein generischer Bibliotheks-User-Agent (Python-urllib, Java/1.8.x auf der Testdomain); die Lösung steht im Abschnitt Testumgebung.Vollständiger Fehlerkatalog
| Code | HTTP | Wann |
|---|---|---|
validation_failed | 400 | Body hat die Validierung nicht bestanden (siehe fields) |
unauthorized | 401 | API-Token fehlt oder ist ungültig |
forbidden | 403 | Aktion erfordert die Rolle owner/admin |
company_not_found | 404 | Firma existiert nicht oder Sie sind nicht Mitglied |
ico_taken | 409 | eine Firma mit diesem IČO existiert bereits |
ico_immutable | 400 | IČO kann nicht geändert werden |
company_dic_missing | 400 | Verifizierung des Versands ohne DIČ (slowakische Steuernummer) der Firma |
token_invalid | 400 | Verifizierungsmerkmal (Verifikačný údaj, Signatur) stimmt nicht |
document_not_found | 404 | Beleg existiert nicht |
token_not_found | 404 | API-Token existiert nicht / gehört nicht Ihnen |
rate_limited | 429 | zu viele Requests |
Rate-Limits
Die API ist durch Rate-Limiting in einem Fenster von 60 Sekunden geschützt (pro Instanz). Limit und Restkontingent liefern wir in jeder Response über Header; bei Überschreitung kommt 429 Too Many Requests mit Retry-After zurück.
- Normale Aufrufe
- dynamisches Limit mit großer Reserve, pro API-Token (oder Session, sonst IP); den aktuellen Wert liefert der Header RateLimit-Limit
- Auth (/auth/*)
- strengeres Limit pro IP (Anti-Brute-Force; außer /auth/me und /auth/logout)
RateLimit-Limit- Limit im Fenster
RateLimit-Remaining- wie viel noch übrig ist
RateLimit-Reset- Sekunden bis zum Zurücksetzen des Fensters
Retry-After- Sekunden bis zum nächsten Versuch (bei 429)
HTTP/2 429 Too Many Requests
RateLimit-Limit: <Limit im Fenster>
RateLimit-Remaining: 0
RateLimit-Reset: 37
Retry-After: 37
{ "error": "rate_limited", "message": "Too many requests. Slow down." }- Limits sind an das Credential gebunden, nicht an die IP: 300 Aufrufe pro Minute pro API-Token (SAPI: pro Access-Token), Auth-Endpunkte 20 pro Minute pro IP und eine Schutzobergrenze von 600 pro Minute pro IP auf /api/v1. Eine serverseitige Integration hinter einer IP ist nicht betroffen; höhere Limits setzen wir auf Anfrage, nennen Sie uns die erwartete Spitze.
- Polling ist ein vollwertiger Weg: GET /sapi/document/sent und /receive mit ?since und ?until alle 1 bis 5 Minuten; Webhooks sind eine Ergänzung, keine Voraussetzung.
- Zeitstempel: Sendezeit = statusDateTime des Status delivered in /sapi/document/sent (bestätigt durch den MLS-Nachweis), Empfangszeit = creationDateTime in /sapi/document/receive; Übergabezeit der FS-Meldung = fsReportedAt (Webhook invoice.reported oder Feld fsReportedAt der invoice.*-Events); jeder Webhook trägt occurredAt und eventId.
- PDF: die API liefert druckbares HTML (…/html, Portal ?format=html), aus dem ein PDF im Browser oder mit Headless Chrome gedruckt wird; einen eigenen PDF-Endpunkt gibt es nicht.
- C#/.NET und andere Sprachen: generieren Sie den Client aus OpenAPI (NSwag, Kiota, openapi-generator); ein eigenes NuGet-Paket liefern wir nicht.
API-Tokens
Opake Tokens vpt_… für den Server-to-Server-Zugriff. Der Klartext wird nur einmal bei der Erstellung angezeigt. Speichern Sie ihn. Wir speichern nur den SHA-256-Hash und ein 12-stelliges Präfix zur Anzeige.
/tokensSession / TokenListe Ihrer aktiven Tokens (ohne Secret), nach createdAt absteigend.
/tokensSession / TokenErstellt einen Token; gibt den Klartext zurück (ein einziges Mal).
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
name | string | ja | Name des Tokens, max. 128 |
curl -X POST https://peppol.verteco.digital/api/v1/tokens -b cookies.txt \
-H 'Content-Type: application/json' -d '{"name":"moja-appka"}'
# 201 Created
{ "id":"…","name":"moja-appka","token":"vpt_8f2a…","prefix":"vpt_8f2a3b…","createdAt":"…" }/tokens/{id}Session / TokenWiderruft den Token. Gibt 204 zurück.
Fehler: token_not_found (404)
Firmen
Firmen (Handelsregisternummer IČO / USt-IdNr. IČ DPH), die Sie verwalten. Der Zugriff ist an die Mitgliedschaft gebunden: Sie sehen nur Firmen, deren Mitglied Sie sind. Wer eine Firma anlegt, wird ihr owner.
/companiessession / tokenListe der Firmen, deren Mitglied Sie sind (mit Ihrer Rolle).
/companiessession / tokenLegt eine Firma an; Sie werden owner, status = pending_verification.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
ico | string | ja | genau 8 Ziffern |
dic | string | ja | UID als SK + 10 Ziffern (z. B. SK2121358349); ein Nicht-Umsatzsteuerzahler sendet nur die 10-stellige DIČ, SK ergänzen wir |
legalName | string | ja | Firmenname, max. 500 |
registeredAddress | string | nein | Firmensitz, max. 1000 |
curl -X POST https://peppol.verteco.digital/api/v1/companies \
-H 'Authorization: Bearer vpt_8f2a…' -H 'Content-Type: application/json' \
-d '{"ico":"53412834","dic":"SK2121358349","legalName":"Verteco digital services, s. r. o."}'
# 201 Created
{ "id":"08dd…","ico":"53412834","dic":"SK2121358349","legalName":"…",
"registeredAddress":null,"peppolParticipantId":null,
"status":"pending_verification","role":"owner","createdAt":"2026-06-03T09:40:27Z" }Fehler: ico_taken (409) · validation_failed (400)
/companies/{id}session / tokenFirmendetails (Sie müssen Mitglied sein).
/companies/{id}owner / adminFirma bearbeiten. Die IČO ist unveränderlich (muss der bestehenden entsprechen).
Fehler: forbidden (403) · ico_immutable (400) · company_not_found (404)
Feld status
pending_verification- nach dem Anlegen; das Senden ist noch nicht freigeschaltet
active- verifiziert, Senden freigeschaltet
Rolle des Mitglieds
owner- Ersteller der Firma (Vollzugriff)
admin- Administrator (Änderungen, Webhooks, Verifizierung)
member- Mitglied (Lesen)
viewer- nur Lesen
owner als auch admin (Firmenmanager) zur Verfügung.Verifizierung für das Senden (Verifizierungsmerkmal)
Vor dem Senden muss die Firma über das Verifizierungsmerkmal (Verifikačný údaj, VÚ) verifiziert werden, ein signiertes Token der Finanzverwaltung der Slowakischen Republik (Finančná správa, FS). Nach erfolgreicher Verifizierung wechselt der status der Firma auf active und das Senden wird freigeschaltet.
/companies/{id}/verificationowner / adminSelf-Verify des Verifizierungsmerkmals. Bei Erfolg wird der status auf active gesetzt.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
token | string | ja | VÚ = Hex-Signatur (prod/pPFS 1024 hex, test/tPFS 768 hex) |
curl -X POST https://peppol.verteco.digital/api/v1/companies/{id}/verification \
-H 'Authorization: Bearer vpt_8f2a…' -H 'Content-Type: application/json' \
-d '{"token":"<1024-hex VÚ>"}'
# 200 OK
{ "companyId":"08dd…","status":"active","sendingVerified":true,
"verificationMethod":"self","verifiedAt":"2026-06-15T…Z" }Fehler: company_dic_missing (400) · token_invalid (400) · forbidden (403)
Dokumente
Protokoll der Peppol-Belege (empfangene und gesendete) je Firma. Es füllt sich, während Rechnungen über den Access Point fließen. An die Mitgliedschaft in der Firma gebunden, sortiert nach createdAt absteigend.
/companies/{id}/documentssession / tokenListe der Belege der Firma. Optionaler Filter ?direction=sent|received.
curl 'https://peppol.verteco.digital/api/v1/companies/{id}/documents?direction=received' \
-H 'Authorization: Bearer vpt_8f2a…'
# 200 OK
[ { "id":"…","direction":"received","peppolMessageId":"…","docTypeId":"…",
"senderId":"0088:7300010000001","receiverId":"0245:2121358349",
"invoiceNumber":"2026001","issueDate":"2026-06-03",
"currency":"EUR","totalAmount":120.00,"status":"received","createdAt":"…" } ]/companies/{id}/documents/{docId}session / tokenDetails eines einzelnen Belegs.
Fehler: document_not_found (404)
/companies/{id}/documents/{docId}/downloadsession / tokenEigenständiges HTML der Rechnung zum Drucken. ?inline=1 → Anzeige im Browser, sonst Download.
Liefert text/html, Content-Disposition mit dem Dateinamen faktura-<číslo>.html (číslo = Rechnungsnummer).
SAPI-SK 1.0 (nationale Schnittstelle)
SAPI-SK ist die standardisierte nationale REST-Schnittstelle zwischen dem Kunden-/ERP-System und dem Access Point (sapi-sk.sk). Wir implementieren sie in vollem Umfang. Dadurch sind Sie nicht an die proprietäre Form unserer API gebunden und schreiben die Integration einmal für jeden beliebigen SAPI-SK Access Point.
- Base URL
https://peppol.verteco.digital/sapi- Authentifizierung
- OAuth2 client_credentials → kurzlebiger Access Token (JWT)
- client_id
- UUID Ihres API-Tokens (Liste im Dashboard-Bereich „API tokeny“, dt. API-Tokens)
- client_secret
- der
vpt_…-Token selbst aus dem Portal - Version
- 1.3 (10 Operationen: 4× Auth, 6× Dokumente)
vpt_-Token des Portals noch die Session). Der Widerruf des API-Tokens im Portal macht sofort sowohl /auth/token als auch /auth/renew ungültig.Sandbox (Probeumgebung)
Möchten Sie SAPI-SK ohne Registrierung und ohne Risiko ausprobieren? Verwenden Sie die öffentlichen Sandbox-Zugangsdaten. Die Sandbox validiert Requests genau wie der Produktivbetrieb, sendet aber niemals etwas in das Peppol-Netzwerk und arbeitet nicht mit realen Daten; sie liefert realistische Mock-Responses. Ideal für Entwicklung, CI und das Onboarding einer Integration.
- client_id
sandbox- client_secret
sandbox- send
- vollständige Validierung des API-Kontrakts + Mock 202 (es wird nichts zugestellt)
- receive
- 1 Beispielbeleg
sandbox-doc-0001zum Testen von Parsing und Acknowledge
# 1) Sandbox-Token (ohne Registrierung)
curl -X POST https://peppol.verteco.digital/sapi/auth/token \
-H 'Content-Type: application/json' \
-d '{ "client_id": "sandbox", "client_secret": "sandbox",
"grant_type": "client_credentials" }'
# 2) Mock-Versand: wird validiert, aber es wird NICHTS real zugestellt
curl -X POST https://peppol.verteco.digital/sapi/document/send \
-H 'Authorization: Bearer <sandbox access_token>' \
-H 'X-Peppol-Participant-Id: 0245:0000000000' \
-H 'Content-Type: application/json' \
-d '{ "metadata": { "documentId": "TEST-1",
"documentTypeId": "urn:…::Invoice##…::2.1",
"senderParticipantId": "0088:sandbox-sender",
"receiverParticipantId": "0088:sandbox-receiver" },
"payload": "<Invoice>…</Invoice>", "payloadFormat": "XML" }'
# 202 { "providerDocumentId": "sandbox-…", "status": "ACCEPTED", … }
# 3) Beispiel-Postfach + Detail des Beispielbelegs
curl https://peppol.verteco.digital/sapi/document/receive \
-H 'Authorization: Bearer <sandbox access_token>'
curl https://peppol.verteco.digital/sapi/document/receive/sandbox-doc-0001 \
-H 'Authorization: Bearer <sandbox access_token>'client_id/client_secret aus dem Portal (unten).Authentifizierung
/sapi/auth/tokenclient_credentialsTauscht client_id + client_secret gegen einen Access Token (15 Min.) und einen Refresh Token (30 Tage). Speichern Sie den Token und verwenden Sie ihn die vollen 15 Minuten: ein neuer Token bei jedem Aufruf ist unnötiger Overhead (das Request-Limit gilt auch für /auth/token).
curl -X POST https://peppol.verteco.digital/sapi/auth/token \
-H 'Content-Type: application/json' \
-d '{ "client_id": "<UUID des Tokens>", "client_secret": "vpt_8f2a…",
"grant_type": "client_credentials" }'
# 200 OK
{ "access_token": "eyJhbGciOi…", "token_type": "Bearer",
"expires_in": 900, "refresh_token": "eyJhbGciOi…" }Fehler: SAPI-AUTH-001 (401) · SAPI-AUTH-003 (401: IP außerhalb der Allowlist des Schlüssels) · SAPI-VAL-001 (400)
/sapi/auth/token/statusBearer (access)Gültigkeit und Ablauf des Access Tokens; should_refresh = true bei < 3 Min. bis zum Ablauf.
/sapi/auth/renewrefresh tokenStellt einen neuen Access + Refresh Token aus. Schlägt fehl, wenn der zugrunde liegende API-Token widerrufen wurde.
// Body
{ "refresh_token": "eyJhbGciOi…" }/sapi/auth/revoke–Gibt immer success zurück (RFC 7009). Dauerhafter Kill-Switch = Widerruf des API-Tokens im Portal.
Versand eines Dokuments
/sapi/document/sendBearer (access)Sendet ein Peppol-Geschäftsdokument (UBL / BIS 3.0) über unseren Access Point an den Empfänger. Der Versand ist fail-closed: die Firma muss ein verifiziertes Verifizierungsmerkmal (Verifikačný údaj) besitzen.
Pflicht-Header:
| Header | Beschreibung |
|---|---|
Authorization | Bearer <access_token> |
X-Peppol-Participant-Id | Teilnehmer, in dessen Namen Sie senden (z. B. 0245:2121358349, also die Ziffern der DIČ, der slowakischen Steuernummer, ohne „SK“) |
Idempotency-Key | eindeutiger Schlüssel pro Sendung; ein wiederholter Aufruf liefert das ursprüngliche Ergebnis und stellt nie doppelt zu. Ausnahme: wurde der erste Versuch schon vor dem Versand abgelehnt (Empfänger nicht im Peppol-Netzwerk, Validierungsfehler, unvollständige Anfrage), führt derselbe Schlüssel den Versand erneut aus, „korrigieren und erneut senden“ funktioniert also unter derselben Rechnungsnummer |
curl -X POST https://peppol.verteco.digital/sapi/document/send \
-H 'Authorization: Bearer eyJ…' \
-H 'X-Peppol-Participant-Id: 0245:2121358349' \
-H 'Idempotency-Key: 7b1f0e2a-…' \
-H 'Content-Type: application/json' \
-d '{ "metadata": {
"documentId": "INV-2026-001",
"documentTypeId": "urn:…::Invoice##…::2.1",
"processId": "urn:…:bis:billing:3.0",
"senderParticipantId": "0245:2121358349",
"receiverParticipantId": "0088:7300010000001",
"creationDateTime": "2026-06-17T10:00:00Z" },
"payload": "<Invoice …>…</Invoice>",
"payloadFormat": "XML" }'
# 202 Accepted
{ "providerDocumentId": "…", "status": "ACCEPTED",
"receivedAt": "2026-06-17T10:00:01Z", "timestamp": "…" }
# bei Status "REJECTED" enthält die Response zusätzlich das Feld "detail"
# mit dem Ablehnungsgrund (Validierungsregeln, z. B. BR-CO-15)metadata dienen dem Routing und sind technisch: documentId ist Ihr interner Identifikator, nicht die Rechnungsnummer. Die Geschäftsdaten (Rechnungsnummer cbc:ID, Ausstellungsdatum, Fälligkeit, Lieferdatum, Währung, Betrag) übernehmen wir direkt aus dem UBL-Payload; nichts davon senden Sie in metadata, und UBL ist stets die Quelle der Wahrheit für die Anzeige im Portal wie auch für Webhooks.Fehler: SAPI-AUTH-002 (401) · SAPI-AUTH-003 (403) · SAPI-VAL-001 (400) · SAPI-PROC-001 (502) · SAPI-PROC-002 (503) · SAPI-PROC-500 (500: nicht wiederholen, melden Sie die correlation_id)
202, das Verdikt steht im Body (status ACCEPTED oder REJECTED). Soll auch der HTTP-Status das Verdikt tragen, senden Sie Prefer: handling=strict (RFC 7240): eine synchrone Ablehnung kommt dann als 422 mit der SAPI-Fehlerhülle zurück (SAPI-VAL-002 bei einem Validierungsfehler, SAPI-RES-003, wenn an den Empfänger nicht zugestellt werden kann; details[] enthalten providerDocumentId und detail), und die Antwort trägt Preference-Applied: handling=strict. ACCEPTED bleibt unverändert. Vor der Übergabe an den Access Point führen wir denselben SML/SMP-Lookup aus wie er. Ein Empfänger, der gar nicht im Peppol-Netz ist, bedeutet ACCEPTED mit undeliverable: true: Wir haben das Dokument übernommen und melden es der Finanzverwaltung unabhängig von der Zustellung (§ 85o Abs. 11, FS FAQ 9/DPH/2025/IM Beispiel 9), zugestellt wird es aber niemandem; auf GET /sapi/document/sent hat es den Status undeliverable, der Webhook erhält invoice.undeliverable, eine E-Mail geht an niemanden, und derselbe Idempotency-Key sendet nach der Registrierung des Empfängers erneut. Ein Empfänger, der im Netz ist, aber den Dokumenttyp nicht veröffentlicht, erhält sofort REJECTED, ohne Zustellversuch. retrying: true bei ACCEPTED bedeutet, dass der erste Zustellversuch fehlschlug (z. B. vorübergehend nicht erreichbares SMP) und der Access Point ihn selbst wiederholt, in der Regel innerhalb von 20 Minuten; detail nennt den Grund, das endgültige Verdikt kommt über GET /sapi/document/sent oder den Webhook./sapi/document/validateBearer (access)Validiert ein Dokument ohne Versand: dieselben Regeln EN 16931 + Peppol BIS 3.0, die der Access Point vor dem Versand anwendet. Nichts wird gespeichert oder versendet; funktioniert auch mit dem Sandbox-Token. Body = das JSON-Paar { payload, payloadFormat } wie bei /document/send oder direkt das XML (Content-Type: application/xml). Limit 10 MB.
curl -X POST https://peppol.verteco.digital/sapi/document/validate \
-H 'Authorization: Bearer eyJ…' \
-H 'Content-Type: application/xml' \
--data-binary @rechnung.xml
# 200 OK
{ "valid": false,
"errors": [ "BR-CO-15: Invoice total amount with VAT (BT-112) = … " ],
"warnings": [],
"checkedAt": "2026-09-10T12:00:00Z" }Fehler: SAPI-AUTH-002 (401) · SAPI-VAL-001 (400: leerer Body, ungültiges JSON, payloadFormat ungleich XML, > 10 MB) · SAPI-SYS-002 (502: Validator vorübergehend nicht verfügbar, erneut versuchen)
Empfang von Dokumenten
/sapi/document/receiveBearer (access)Liste der empfangenen Dokumente (älteste zuerst); die Metadaten enthalten auch invoiceNumber, sodass Sie den Beleg ohne Download des Payloads identifizieren. Query: ?pageToken, ?limit (max. 200), ?status (received / acknowledged; unabhängig von Groß-/Kleinschreibung, ein anderer Wert liefert den Fehler SAPI-VAL-001), ?invoiceNumber (exakte Übereinstimmung mit cbc:ID), ?since und ?until (ISO-8601-Instant; Zeitfenster nach Empfangszeitpunkt, z. B. Belege der letzten 5 Tage, Massendownload für eine externe Archivierung oder die Rekonstruktion der Buchhaltung), ?deliveryDateFrom und ?deliveryDateTo (ISO-8601-Datum; Filter nach dem Lieferdatum aus dem Beleg). Der Header X-Peppol-Participant-Id ist Pflicht.
// 200 OK
{ "documents": [ { "documentId":"…","documentTypeId":"…",
"senderParticipantId":"0088:…","receiverParticipantId":"0245:…",
"creationDateTime":"2026-06-17T…Z" } ],
"nextPageToken": "50" }/sapi/document/receive/{documentId}Bearer (access)Detail inklusive Payload (Roh-XML, so wie es über Peppol eingegangen ist).
Fehler: SAPI-RES-001 (404) · SAPI-RES-002 (404: Payload nicht archiviert)
/sapi/document/receive/{documentId}/acknowledgeBearer (access)Bestätigt die Übernahme des Dokuments durch Ihr System. Idempotent.
/sapi/document/receive/{documentId}/xmlBearer (access)Archivlink (metadata.links.xml): das Geschäftsdokument selbst als XML-Datei, gleicher Inhalt wie der Payload im Detail.
/sapi/document/receive/{documentId}/htmlBearer (access)Archivlink (metadata.links.html): generische druckbare Darstellung der Rechnung (HTML).
/sapi/document/receive/{documentId}/pdfBearer (access)Archiv-Link (metadata.links.pdf): das PDF des Belegs. Hat der Lieferant sein eigenes Rechnungs-PDF ins XML eingebettet (BT-125), erhalten Sie dieses Original; sonst ein auf Anfrage aus dem gespeicherten XML erzeugtes PDF (kein PDF wird gespeichert). Header X-Verteco-Pdf-Source: supplier | generated. 404 SAPI-RES-002 nach Löschung des Inhalts, 503 bei nicht verfügbarem Renderer (später erneut oder /html).
/sapi/document/receive/{documentId}/attachments/{index}Bearer (access)Archivlink (metadata.links.attachments[].url): die Bytes eines im Dokument eingebetteten Anhangs, z. B. das PDF-Original des Lieferanten. Erzwungener Download.
/sapi/document/receive/{documentId}/public-linkBearer (access)Link ohne Anmeldung (optional): erzeugt einen NEUEN Zufallsschlüssel für den Beleg ({"rotate":true} ersetzt den bestehenden). Liefert url (Seite), xmlUrl, htmlUrl, pdfUrl und expiresAt; der Schlüssel wird nur einmal angezeigt. Die Firma muss „Download ohne Anmeldung“ aktiviert haben (sonst 403 SAPI-AUTH-004). DELETE widerruft ihn.
Status gesendeter Dokumente
/sapi/discovery?receiverId=0245:2121358349Preflight vor dem Versand: Ist der Empfänger im Peppol-Netzwerk registriert und welche Belegtypen kann er empfangen? Derselbe SML/SMP-Lookup, den auch der Access Point durchführt; receiverId akzeptiert eine Peppol-ID, eine USt-IdNr. (IČ DPH) sowie die DIČ selbst. Response: {registered, participantId, smp, documentTypes, checkedAt}. Der Header X-Peppol-Participant-Id ist hier nicht erforderlich.
/sapi/document/sentBearer (access)Zustellstatus gesendeter Belege: pending (Übergabe an das Netzwerk) → submitted → delivered / rejected (bestätigt durch den Zustellnachweis (MLS) vom AP des Empfängers); failed = Transportfehler (ein Retry mit demselben Idempotency-Key sendet erneut). Query: ?pageToken, ?limit, ?status (pending / submitted / sent / delivered / rejected / failed; unabhängig von Groß-/Kleinschreibung, ein anderer Wert liefert den Fehler SAPI-VAL-001), ?invoiceNumber (exakte Übereinstimmung mit cbc:ID: der Status einer konkreten Rechnung mit einem einzigen Aufruf), ?since und ?until (ISO-8601-Instant; Zeitfenster), ?deliveryDateFrom und ?deliveryDateTo (ISO-8601-Datum; Filter nach Lieferdatum).
// 200 OK
{ "documents": [ {
"documentId": "…",
"receiverParticipantId": "0245:1084695645",
"peppolMessageId": "befc9112-…",
"status": "delivered",
"statusDateTime": "2026-07-09T14:52:31Z",
"creationDateTime": "2026-07-09T14:52:12Z" } ],
"nextPageToken": null }Fehler: SAPI-AUTH-002 (401) · SAPI-AUTH-003 (403) · SAPI-VAL-001 (400: ungültiges since)
Massenversand
/sapi/document/batchBearer (access)Bis zu 100 Dokumente in einem Aufruf. Jedes Element durchläuft die vollständige Logik des Einzelversands (Idempotenz über itemId + idempotencyKey, Reservierung, Submit, Verdikt) und liefert ein eigenes Ergebnis; der Fehler eines Elements stoppt die übrigen nicht. Die Elemente werden sequenziell verarbeitet; failed-Elemente wiederholen Sie mit demselben idempotencyKey.
// Request
{ "documents": [ {
"itemId": "fa-2026-001",
"idempotencyKey": "fa-2026-001",
"metadata": { "documentId": "2026001", "documentTypeId": "…", "processId": "…",
"senderParticipantId": "0245:2121358349", "receiverParticipantId": "0245:1084695645" },
"payload": "<Invoice …>", "payloadFormat": "XML" } ] }
// 202 Accepted
{ "total": 2, "accepted": 1, "rejected": 1, "failed": 0,
"results": [
{ "itemId": "fa-2026-001", "ok": true, "providerDocumentId": "…", "status": "ACCEPTED" },
{ "itemId": "fa-2026-002", "ok": false, "errorCode": "SAPI-AUTH-003", "errorMessage": "…" } ] }Fehler: SAPI-AUTH-002 (401) · SAPI-AUTH-003 (403) · SAPI-VAL-001 (400: leere/zu große Liste)
Fehlermodell
Alle SAPI-Fehler haben einen einheitlichen Envelope mit Kategorie, stabilem Code, dem Flag retryable und einer correlation_id für den Support.
{ "error": {
"category": "AUTH",
"code": "SAPI-AUTH-001",
"message": "Invalid client credentials.",
"retryable": false,
"correlation_id": "b4191dfc-…" } }Benachrichtigungen & Webhooks
Bei einer empfangenen Rechnung können wir die Firma per E-Mail oder per Webhook (POST an Ihre URL) benachrichtigen. Die Zustellung ist durable: Sie wird in eine Ausgangsqueue geschrieben und asynchron zugestellt (15 s Timeout); bei einem Fehlschlag wiederholen wir bis zu 8× mit exponentiellem Backoff (30 s → max. 1 h), danach Dead-Letter. Ein Ausfall Ihres Servers verliert die Benachrichtigung also nicht, wir stellen sie beim nächsten Versuch zu. Der SSRF-Schutz blockiert Loopback-/lokale Adressen; die Webhook-URL muss öffentlich erreichbar sein.
/companies/{id}/notificationssession / tokenBenachrichtigungseinstellungen: { webhookUrl, notificationEmail, hasSecret }.
/companies/{id}/notificationsowner / adminSetzt beide Kanäle (leerer String = hebt den jeweiligen Kanal auf).
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
webhookUrl | string | nein | leer oder http(s)-URL, max. 512 |
notificationEmail | string | nein | gültige E-Mail-Adresse, max. 256 |
/companies/{id}/webhooksession / tokenWebhook-Konfiguration: { url, hasSecret } (ohne Secret).
/companies/{id}/webhookowner / adminSetzt die Webhook-URL (generiert automatisch ein Signatur-Secret, falls noch keines existiert).
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
url | string | ja | http(s)-URL, max. 512 |
curl -X PUT https://peppol.verteco.digital/api/v1/companies/{id}/webhook \
-H 'Authorization: Bearer vpt_8f2a…' -H 'Content-Type: application/json' \
-d '{"url":"https://vas-system.sk/peppol/webhook"}'
# 200 OK { "url":"https://vas-system.sk/peppol/webhook", "hasSecret": true }/companies/{id}/webhookowner / adminEntfernt Webhook-URL und Secret. Gibt 204 zurück.
/companies/{id}/webhook/secretowner / adminGeneriert ein neues Signatur-Secret und gibt es EINMALIG zurück; speichern Sie es zur Prüfung von X-Verteco-Signature.
// 200 OK
{ "secret": "vpt_8f2a…" }/companies/{id}/webhook/testowner / adminSendet synchron eine Testbenachrichtigung (Event webhook.test, signiert, SSRF-geschützt) an die gespeicherte URL und gibt zurück, WAS gesendet wurde und WAS zurückkam. Dasselbe lösen Sie mit der Schaltfläche „Otestovať webhook“ (Webhook testen) in den Firmendetails aus.
// 200 OK
{
"sent": { "url": "https://vas-system.sk/peppol/webhook", "event": "webhook.test",
"signed": true, "payload": "{…}" },
"received": { "status": 200, "body": "OK", "durationMs": 142, "error": null }
}
// 400 webhook_not_configured, wenn keine Webhook-URL gespeichert istWebhook-Payload
Sowohl bei einer empfangenen (invoice.received) als auch bei einer gesendeten (invoice.sent) Rechnung senden wir einen POST mit diesem Body; das Feld event unterscheidet den Typ.
Zwei weitere Events melden das Urteil des Netzwerks zu einer Rechnung, die Sie gesendet haben: invoice.delivered (der Access Point des Empfängers hat die Zustellung mit einem Zustellnachweis (MLS) bestätigt) und invoice.rejected (abgelehnt, entweder durch das Netzwerk oder bereits bei der Validierung vor dem Versand). Sie tragen die Identifikation des Belegs und der Firma (documentId, invoiceNumber, receiverId, peppolMessageId, companyDic, peppolParticipantId) sowie status, statusDetail mit dem Ablehnungsgrund und statusDateTime. Ist der erste Zustellversuch fehlgeschlagen und wiederholt ihn der Access Point selbst (in der Regel innerhalb einer Stunde), bleibt das Dokument submitted und statusDetail beginnt mit network_retrying: ; eine Zustellung ergibt dann delivered, ein Aufgeben rejected mit dem Präfix network_gave_up: . Dank ihnen müssen Sie den Status gesendeter Rechnungen nicht per Polling abfragen.
Das Event company.activated kommt, wenn ein Kunde die Auswahl des Anbieters im FS-Portal (VPDS) der Finanzverwaltung der Slowakischen Republik (Finančná správa, FS) abschließt (Body: event, companyId, companyDic, peppolParticipantId, status, verifiedAt); company.deactivated dagegen bei der Abmeldung der Firma aus dem Netzwerk (gleicher Body ohne verifiedAt). company.smp_registered_elsewhere kommt, wenn eine Firma die FS-Auswahl abgeschlossen hat, ihr Eintrag im nationalen SMP aber von einem anderen Anbieter gehalten wird (Body wie company.activated plus action: "migration_code_required" und migrateUrl). Das Vorgehen für Partner steht in der Vermittler-Dokumentation. Bis der Kunde einen Migrationscode eingibt, stellt das Netzwerk an den alten Anbieter zu. Details in der Anleitung /saas:
{
"event": "invoice.received",
"eventId": "7f1c2d9e-4b1a-4e3d-9c1f-0a2b3c4d5e6f",
"occurredAt": "2026-09-03T15:04:05Z",
"companyId": "08dd…",
"documentId": "…",
"invoiceNumber": "2026001",
"senderId": "0088:7300010000001",
"supplierName": "Dodávateľ s.r.o.",
"receiverId": "0245:2121358349",
"issueDate": "2026-06-03",
"dueDate": "2026-06-17",
"deliveryDate": "2026-06-03",
"currency": "EUR",
"totalAmount": "120.00",
"peppolMessageId": "…",
"companyDic": "SK2121358349",
"peppolParticipantId": "0245:2121358349"
}Die Felder companyDic und peppolParticipantId identifizieren die Firma, auf die sich das Event bezieht (wichtig für Partner, die eine einzige Webhook-URL für alle ihre Kunden verwenden).
Urteil des Netzwerks zu einer gesendeten Rechnung:
{
"event": "invoice.delivered", // oder "invoice.rejected"
"occurredAt": "2026-09-03T15:04:05Z",
"companyId": "08dd…",
"companyDic": "SK2121358349",
"peppolParticipantId": "0245:2121358349",
"documentId": "…",
"invoiceNumber": "2026001",
"receiverId": "0245:2120049096",
"peppolMessageId": "…",
"status": "delivered", // oder "rejected"
"statusDetail": null, // bei rejected: Ablehnungsgrund
"statusDateTime": "2026-06-03T10:15:42Z"
}Aktivierung der Firma nach der Auswahl im FS-Portal (VPDS):
{
"event": "company.activated", // company.deactivated hat denselben Body ohne verifiedAt
"occurredAt": "2026-09-03T15:04:05Z",
"companyId": "08dd…",
"companyDic": "SK2121358349",
"peppolParticipantId": "0245:2121358349",
"status": "active",
"verifiedAt": "2026-06-03T10:02:11Z"
}Den Event-Namen trägt auch der Header X-Verteco-Event. Vollständige Liste der Events: invoice.received, invoice.sent, invoice.delivered, invoice.rejected, invoice.undeliverable, invoice.reported, company.activated, company.deactivated, company.smp_registered_elsewhere sowie das Test-Event webhook.test. Einen unbekannten Event-Typ sollten Sie beiseitelegen und protokollieren; einen neuen Typ kündigen wir immer im Voraus an. Jedes Event trägt zusätzlich occurredAt (Ereigniszeit, ISO-8601 UTC) zum Sortieren sowie eventId (derselbe Wert wie im Header X-Verteco-Delivery-Id): eindeutig pro Event und bei jeder Wiederholung unverändert, also der richtige Schlüssel zum Deduplizieren (peppolMessageId wiederholt sich über invoice.sent, invoice.delivered und invoice.reported). invoice.*-Events tragen zusätzlich die Zeiten sentAt, deliveredAt, receivedAt und fsReportedAt (ISO-8601 UTC, null, bis das Ereignis eingetreten ist). invoice.reported kommt, wenn die Steuermeldung an die Finanzverwaltung (TDD) zum Beleg dem Zustellnetz übergeben wurde (§ 85o Abs. 11), bei einem Ausfall des C5 auch Tage später. Formale Schemata aller Events stehen im Abschnitt webhooks der OpenAPI-Spezifikation.
Signaturprüfung
Hat die Firma ein Secret, senden wir den Header X-Verteco-Signature in der Form sha256=HMAC-SHA256(secret, raw Body) (lowercase hex). Berechnen Sie den HMAC immer über die exakten Bytes des Bodys:
import crypto from 'node:crypto';
// rawBody = die exakten Bytes des Request-Bodys (kein erneut serialisiertes JSON)
function verify(rawBody, header, secret) {
const expected = 'sha256=' +
crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}Header jeder Zustellung: X-Verteco-Event (Event-Name), X-Verteco-Delivery-Id (= eventId im Body, bei jeder Wiederholung identisch), X-Verteco-Signature (der HMAC oben) und parallel die Standard-Webhooks-Header: webhook-id (= eventId), webhook-timestamp (Unix-Sekunden dieses Versuchs) und webhook-signature = v1,base64(HMAC-SHA256(secret, id + "." + timestamp + "." + body)). Schlüssel sind die UTF-8-Bytes Ihres Secrets; eine standardwebhooks-Bibliothek erwartet es als whsec_ + base64(secret). Empfohlene Verarbeitung: Signatur prüfen, Zustellungen mit einem webhook-timestamp älter als 5 Minuten ablehnen (Replay-Schutz), idempotent nach eventId verarbeiten, innerhalb von 15 Sekunden mit 2xx antworten und schwere Arbeit in die eigene Queue verlagern. Eine fehlgeschlagene Zustellung wiederholen wir 8-mal im Abstand von 30 s bis 1 h; danach landet sie im Dead-Letter mit E-Mail-Hinweis und bleibt im Realtime-Log sichtbar. Eine produktive Webhook-URL muss https:// sein; die Testumgebung akzeptiert auch http://.
POST /companies/{id}/webhook/secret; es wird einmalig zurückgegeben (eine Rotation erzeugt ein neues). Ohne Secret senden wir den Header X-Verteco-Signature nicht.Massenkonfiguration: ein Token, mehrere Firmen
Ein einziger API-Token (an Ihr Konto/Ihre E-Mail gebunden) verwaltet alle Firmen, die dieses Konto besitzt: Wer eine Firma über POST /companies anlegt, wird deren owner und kann ihren Webhook einrichten. Der Webhook gilt pro Firma (eigene URL und eigenes Secret), sodass Sie mit demselben Token beliebig viele Firmen anbinden:
# 1) Liste Ihrer Firmen (seitenweise, siehe Firmen)
curl 'https://peppol.verteco.digital/api/v1/companies?page=0&limit=100' -H 'Authorization: Bearer vpt_8f2a…'
# 2) für JEDE Firma {id}: Webhook (und/oder E-Mail) setzen
curl -X PUT https://peppol.verteco.digital/api/v1/companies/{id}/notifications \
-H 'Authorization: Bearer vpt_8f2a…' -H 'Content-Type: application/json' \
-d '{"webhookUrl":"https://vas-system.sk/peppol/webhook","notificationEmail":"faktury@firma.sk"}'
# 3) Signatur-Secret abholen (wird NUR EINMAL zurückgegeben) und für die Signaturprüfung speichern
curl -X POST https://peppol.verteco.digital/api/v1/companies/{id}/webhook/secret -H 'Authorization: Bearer vpt_8f2a…'
# → { "secret": "…" }- Der Token muss
owner/adminder jeweiligen Firma sein; bei einer Firma, in der er nur member/viewer ist, kommt403 forbiddenzurück (kein Cross-Tenant-Zugriff). - Sie können jeder Firma eine andere URL geben oder allen dieselbe; im Payload unterscheiden Sie sie anhand von
companyId(undreceiverId). - Bei Hunderten von Firmen beachten Sie das Rate-Limit pro Token (den aktuellen Wert liefert der Header
RateLimit-Limit); arbeiten Sie in Batches mit Backoff bei429(HeaderRetry-After). - Der Webhook wird erst dann tatsächlich ausgelöst, wenn die Firma in Peppol aktiv ist (nach der Auswahl von Verteco als Anbieter bei der Finanzverwaltung) und somit tatsächlich Belege empfängt.
Datenmodelle
Felder der von der API zurückgegebenen Objekte.
Company
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
id | UUID | – | Kennung der Firma |
ico | string | – | Handelsregisternummer (IČO, 8 Ziffern) |
dic | string|null | – | USt-IdNr. (IČ DPH) |
legalName | string | – | Firmenname |
registeredAddress | string|null | – | Sitz |
peppolParticipantId | string|null | – | Teilnehmer im Peppol-Netzwerk (nach der Registrierung) |
status | string | – | pending_verification | active |
role | string | – | owner | admin | member | viewer (Ihre Rolle) |
createdAt | Instant | – | Zeitpunkt der Erstellung |
Document
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
id | UUID | – | Kennung des Belegs |
direction | string | – | sent | received |
peppolMessageId | string|null | – | ID der Peppol-Nachricht |
docTypeId | string|null | – | Dokumenttyp (Peppol) |
senderId | string|null | – | Absender (scheme:id) |
receiverId | string|null | – | Empfänger (scheme:id) |
invoiceNumber | string|null | – | Rechnungsnummer |
issueDate | date|null | – | Ausstellungsdatum |
currency | string|null | – | Währung (z. B. EUR) |
totalAmount | number|null | – | Betrag |
status | string | – | Verarbeitungsstatus |
createdAt | Instant | – | Zeitpunkt der Erfassung |
User · Token · Webhook
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
User | Objekt | – | { id: UUID, email: string } |
Token | Objekt | – | { id, name, prefix, lastUsedAt|null, createdAt } |
Webhook | Objekt | – | { url: string|null, hasSecret: boolean } |
Slowakische Konventionen aus der Praxis der Implementierer
Über Peppol BIS Billing 3.0 hinaus einigen sich die slowakischen Hersteller von ERP- und Fakturierungssystemen laufend auf eine gemeinsame Interpretation optionaler Felder (die Diskussion läuft im Slack-Kanal der Finanzverwaltung der Slowakischen Republik, Finančná správa, FS). Nachfolgend die Konventionen, die unser Portal bereits heute berücksichtigt: Alles sind gültige BIS-Konstrukte, sie passieren unsere API unverändert, und empfangene Belege zeigen sie auch in der menschenlesbaren Vorschau und im PDF an.
- Abzug einer versteuerten Anzahlung auf Positionsebene: eine negative Position mit
cac:DocumentReference, wobeicbc:IDdie Nummer des Steuerbelegs zur erhaltenen Zahlung trägt undcbc:DocumentTypeCodeden Wert130hat (BIS: invoice line object identifier, max. 1 pro Position). Eine empfangene Rechnung mit einer solchen Position wird in unserer Vorschau mit dem Hinweis „odpočet zálohy – daňový doklad č. …" (Abzug der Anzahlung, Steuerbeleg Nr. …) angezeigt. - Steuerbeleg zur erhaltenen Zahlung: gemäß den FAQ der Finanzverwaltung wird
InvoiceTypeCode 388(Tax invoice) verwendet. Er passiert sowohl unsere API als auch die Validierung; der Abzug einer nicht bezahlten (nicht versteuerten) Anzahlung wird übercbc:PrepaidAmountausgedrückt. - Stornierung einer Rechnung: Gutschrift
381(CreditNote) mitcac:BillingReferenceauf die ursprüngliche Rechnung, keine negative Rechnung 380. Achtung: Den Code 384 lehnt das Netzwerk für slowakische Parteien ab (die Regel PEPPOL-EN16931-P0112 erlaubt ihn nur zwischen deutschen Subjekten); für eine Korrektur nach oben dient die Belastungsanzeige 383. - BT-83 PaymentID: in der slowakischen Praxis setzt sich für die Zahlerreferenz das Format
/VS…/SS…/KS…durch; das variable Symbol allein ist ebenfalls üblich. Unsere Verarbeitung überträgt den Wert unverändert und zeigt ihn bei den Zahlungsangaben an. - Zusätzliche Positionsangaben (Chargen, Seriennummern, Verfallsdaten) über
cac:AdditionalItemPropertymit etablierten Bezeichnungen wieBatchNumber,SerialNumber,ExpirationDate.
Anlagen zu Belegen (BT-125)
An eine E-Rechnung können Anlagen (PDF, Bilder) als base64 im Element cac:AdditionalDocumentReference (BT-125) angehängt werden. Unser Limit beträgt 25 MB pro Anlage. Peppol legt kein einheitliches netzwerkweites Limit fest, die einzelnen Anbieter bestimmen es selbst (FS FAQ 9/DPH/2025/IM, Beispiel Nr. 67); prüfen Sie daher bei sehr großen Anlagen auch das Limit des Anbieters der Gegenseite.
Öffentliche Werkzeuge (Validierung, Empfängerprüfung)
Zwei Hilfs-Endpoints ohne Authentifizierung: derselbe Validierungskern und derselbe SML/SMP-Lookup, die auch unser Access Point verwendet. Sie eignen sich für CI ebenso wie für Prüfungen vor dem Versand; für sie gilt ein strengeres öffentliches Rate Limit.
/public/peppol-validateöffentlichValidierung einer E-Rechnung gegen EN 16931 + Peppol BIS Billing 3.0 (einschließlich der slowakischen Regeln), dasselbe wie der UI-Validator unter /validator. Request-Body = direkt das UBL 2.1 XML (Invoice / CreditNote, ggf. das gesamte Peppol SBD), Content-Type application/xml, Limit 3 MB.
curl -X POST https://peppol.verteco.digital/api/v1/public/peppol-validate \
-H "Content-Type: application/xml" \
--data-binary @faktura.xml// 200 OK
{
"valid": false,
"errors": [
"BR-CO-09: [BR-CO-09]-The Seller VAT identifier (BT-31) … shall have a prefix in accordance with ISO code…"
],
"warnings": []
}
// 400 = leerer Body (empty_document) oder Dokument über 3 MB (document_too_large)/public/peppol-check?id=0245:2121358349öffentlichPrüfung des Empfängers im produktiven Peppol-Netzwerk (SML/SMP-Lookup): Ist er registriert, und welche Belegtypen kann er empfangen? Der Parameter id akzeptiert eine Peppol ID (0245:…), eine USt-IdNr. (IČ DPH, SK…) sowie die reine DIČ (slowakische Steuernummer). Die Bezeichnungen in capabilities werden auf Slowakisch zurückgegeben. Authentifiziertes Gegenstück für ERP-Pipelines: GET /sapi/discovery.
// 200 OK
{
"registered": true,
"participantId": "0245:2121358349",
"smp": "sml.peppol-smp.sk",
"capabilities": ["Faktúra (BIS Billing)", "Dobropis", "Self-billing", "MLS doručenky"],
"lastCheckedAt": "2026-08-31T…Z"
}MCP-Server (KI-Assistenten)
Das Portal betreibt einen eigenen Server für das Model Context Protocol, den offenen Standard, über den KI-Assistenten (Claude Code, Claude Desktop, Cursor, VS Code Copilot und andere) externe Systeme anbinden. Der Assistent meldet sich mit einem gewöhnlichen API-Schlüssel des Kontos an und erhält sechs Werkzeuge; er sieht nie mehr als dieses Konto, und jeder Aufruf erscheint im API-Log des Schlüssels.
Adresse: https://peppol.verteco.digital/api/mcp. Transport: Streamable HTTP, JSON-RPC 2.0, zustandslos (POST /api/mcp, kein SSE-Stream, GET antwortet 405). Anmeldung mit dem Header Authorization: Bearer <API-Schlüssel>. Unterstützte Methoden: initialize, ping, tools/list, tools/call, resources/list, prompts/list.
Werkzeuge
list_companies: Firmen des Kontos mit Peppol-ID, Status und Rolle (Beginn jedes Gesprächs)list_documents: gesendete/empfangene Rechnungen einer Firma mit Zustell- und Meldestatus, seitenweiseget_document: eine Rechnung im Detail samt MLS-Quittung und Meldung, optional das UBL-XML (bis 1 MB)check_participant: Live-Prüfung des Empfängers im Peppol-Netz (SML/SMP)validate_document: UBL-Validierung gegen EN 16931 + BIS 3.0 einschließlich der slowakischen Regelnsend_document: Versand einer Rechnung im Namen der Firma, erfordert confirm = true, die Versandfreigabe (Verifikačný údaj) gilt
Anbindung in Claude Code
claude mcp add --transport http verteco-peppol https://peppol.verteco.digital/api/mcp \
--header "Authorization: Bearer vpt_..."Claude Desktop (über die Brücke mcp-remote, benötigt Node.js)
{
"mcpServers": {
"verteco-peppol": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://peppol.verteco.digital/api/mcp", "--header", "Authorization: Bearer vpt_..."]
}
}
}Der Versand einer Rechnung ist rechtlich bindend: Das Werkzeug lehnt einen Aufruf ohne confirm = true ab, und die Serveranweisungen verpflichten den Assistenten, die ausdrückliche Zustimmung des Nutzers zur konkreten Rechnung und zum Empfänger einzuholen. Fertige Konfigurationen für Cursor und VS Code sowie eine Schaltfläche zum Erstellen des Schlüssels finden Sie im Portal: API-Schlüssel → Reiter MCP-Server. API-Schlüssel → MCP-Server
Status (ping)
Öffentlicher Health-Check-Endpoint, geeignet für das Monitoring.
/pingöffentlichStatus des Backends.
// 200 OK
{ "service": "peppol-portal-backend", "status": "ok", "timestamp": "2026-06-17T…Z" }Eine Live-Übersicht aller Komponenten finden Sie auf der Systemstatus-Seite.
In Vorbereitung
- POST
/companies/{id}/peppol/register· Manuelle Registrierung im Peppol SMP über die API. Heute erfolgt sie automatisch bei der Auswahl des Anbieters im FS-Portal (VPDS) der Finanzverwaltung der Slowakischen Republik (Finančná správa, FS).
POST /companies/{id}/documents/send ist bereits verfügbar (Beta: die Form der Response kann sich noch ändern); für den stabilen programmatischen Versand empfehlen wir SAPI-SK POST /sapi/document/send mit Idempotency-Key.Möchten Sie frühzeitigen Zugang zur Integration, eine Sandbox oder haben Sie eine Frage? Melden Sie sich direkt:
