← Back to all articles

Von Daten zum versiegelten PDF in einem einzigen API-Call

SealDoc Team · · 5 min read

Die Frage, die wir am häufigsten von Kunden mit einem funktionierenden Automation-Stack hörten, war kurz: “Warum muss ich meine Rechnung erst woanders rendern, bevor ich sie Ihnen übergeben kann?” Eine berechtigte Frage. Ein n8n- oder Make-Flow, der aus einer Datenbankzeile oder einem Webhook-Payload ein sauberes Compliance-PDF machen wollte, blieb bisher genau an dieser Stelle stecken. Sie hatten Daten. Sie wollten ein versiegeltes PDF/A-3. Dazwischen saß ein Drittanbieter mit eigenem Preisschild, eigenem Rate Limit und eigenem Template-System, das sich bei jeder Regeländerung mit unserem Intake-Validator stritt.

Diese Zwischenstation ist weg. Seit Sprint 49 und 50 können Sie Markdown oder eine JSON-Rechnung direkt an SealDoc schicken und Sie bekommen ein PDF/A-3 zurück, das denselben Regeln entspricht wie alles, was durch unsere Custody-Pipeline läuft: Archivformat, eingebettetes XML wo zutreffend, optionaler RFC 3161-Zeitstempel, optionales Evidence Pack, Idempotency-Key-Unterstützung. Ein HTTP-Request, eine Antwort, fertig für Ihren Retention-Bucket.

Was neu ist

Zwei neue Endpoints und eine Härtungsmaßnahme in den Evidence Packs.

POST /api/documents/generate

Schicken Sie Markdown oder HTML, bekommen Sie ein PDF/A-3 zurück. Der HTML-Sanitizer (siehe ADR-0013) entfernt serverseitig sämtliche Scripts, iframes, externen Bilder und CSS-url()-Verweise, denn ein Dokument, das Sie archivieren, darf seinen Inhalt nicht später still über ein externes Asset ändern. Maximal 100KB Inhalt pro Request: groß genug für einen typischen Compliance-Bericht, klein genug, um Missbrauch zu verhindern. Idempotency-Key wird unterstützt, was bedeutet, dass ein Retry aus Ihrem n8n-Flow keine zweite PDF in Ihrem Archiv erzeugt.

curl -X POST https://api.sealdoc.eu/api/documents/generate \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: report-2026-q1-acme-001" \
  -d '{
    "format": "markdown",
    "content": "# Q1 2026 Compliance Report\n\nTenant: Acme BV\n\nAll 142 invoices processed without exceptions.",
    "title": "Q1 2026 Compliance Report",
    "timestampRfc3161": true,
    "generateEvidencePack": true
  }'

POST /api/invoices/generate

Gleiches Prinzip, aber für Rechnungen. Sie schicken einen strukturierten JSON-Payload (Verkäufer, Käufer, Positionen, Summen, Daten) und bekommen ein Factur-X 1.0 BASIC PDF/A-3 zurück, in dem die CII-XML bereits eingebettet ist. Kein Zwischenschritt mehr, in dem Sie zuerst eine visuelle PDF rendern müssen, die wir danach semantisch matchen müssen.

Zwei Dinge sollten Sie wissen, bevor Sie das produktiv schalten.

VAT-leading Verkäufervalidierung. Die USt-Nummer in seller.vatNumber muss mit der USt-Nummer Ihres eigenen Tenants übereinstimmen. Wir kanonisieren beide Seiten (Großbuchstaben, Punkte und Leerzeichen entfernt) vor dem Vergleich, also passt nl 1234.56789.b01 problemlos zu NL123456789B01. Stimmen sie nicht überein, bekommen Sie eine 403 mit klarer Fehlerkennung. Das ist keine Bürokratie: es verhindert, dass ein Tenant Rechnungen im Namen eines anderen ausstellt. CompanyName-Drift zwischen Request und Tenant blockieren wir nicht (Handelsnamen ändern sich), wir loggen es aber als Audit-Warnung, damit es nachvollziehbar bleibt.

Billing-Profil muss vollständig sein. USt, Adresse, PLZ, Stadt und Land in Ihrem Tenant bilden zusammen die Verkäuferidentität auf der Rechnung. Fehlt eines davon, bekommen Sie eine 412 mit der Liste der fehlenden Felder. Das stellen Sie einmal im Portal richtig und danach nie wieder.

curl -X POST https://api.sealdoc.eu/api/invoices/generate \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "seller": {"name":"Acme BV","vatNumber":"NL123456789B01","address":"Singel 1","postalCode":"1011AB","city":"Amsterdam","country":"NL"},
    "buyer": {"name":"Klant BV","vatNumber":"BE0123456789","address":"Avenue 5","postalCode":"1000","city":"Brussels","country":"BE"},
    "invoiceNumber":"2026-001","invoiceDate":"2026-05-07","currency":"EUR","vatRate":0.21,"vatLabel":"21% BTW",
    "lines":[{"description":"Consulting","quantity":10,"unitPriceExclVat":150,"unit":"hour"}],
    "timestampRfc3161":true,"generateEvidencePack":true
  }'

Für wiederkehrende Abrechnungen gilt: schicken Sie immer einen expliziten Idempotency-Key. Wir deduplizieren nicht automatisch auf Payload-Hash, denn zwei identische Monatsrechnungen innerhalb von 24 Stunden können vollkommen legitim sein (Korrektur, Neuausstellung, doppeltes Abonnement bei einer juristischen Person). Idempotency-Key in der Hand Ihres Flows, Garantie gegen Duplikate in unserer.

Manifest Hash-Chain v2.0 in Evidence Packs

Eine weniger sichtbare, aber für Auditoren wichtige Härtung. Jedes Evidence Pack enthält jetzt eine MANIFEST.json mit SHA256 und Bytegröße pro Datei, alphabetisch nach Namen sortiert, in kanonischem JSON (camelCase, ohne Einrückung). Daneben liegt eine manifest.sha256: der SHA256 dieser kanonischen Manifest-Bytes.

Warum zwei Ebenen. Die Hashes pro Datei erkennen inhaltliches Tampering an einer einzelnen Datei. Der Manifest-Hash erkennt Tampering am Dateisatz selbst: das stille Entfernen einer Anlage aus dem ZIP und das entsprechende Umschreiben des Manifests. Letzteres war unter v1.0 noch möglich; unter v2.0 nicht mehr, ohne dass ein Verifier es bemerkt.

Wichtig: das ist keine kryptographische Signatur, und genau deshalb heißt sie nicht manifest.signature. Die Benennung spiegelt wider, was es ist, eine Hash-Chain. Ein zukünftiger Sprint fügt möglicherweise Cosign oder einen von SealDoc verwalteten Schlüssel für Non-Repudiation hinzu; ab dann wird manifest.signature der korrekte Name, und nicht früher.

Ein echtes Beispiel: Stripe-Webhook zu archivierter Factur-X

Konkret, so eröffnen die drei Änderungen zusammen eine Klasse von Workflows, die es vorher nicht gab.

Angenommen, Sie verkaufen SaaS-Abonnements über Stripe. Bei jedem erfolgreichen invoice.payment_succeeded-Webhook wollen Sie eine deutsche oder niederländische Factur-X-Rechnung in Ihrem eigenen Archiv haben, getrennt von dem, was Stripe erzeugt (Stripes PDF ist kein PDF/A-3, kein Factur-X und hat keinen RFC 3161-Zeitstempel).

Der Flow in n8n oder Make:

  1. Stripe Trigger fängt invoice.payment_succeeded ab.
  2. Function-Node mappt das Stripe-Objekt auf einen SealDoc-Invoice-Payload. customer_address wird zu buyer, lines wird aus lines.data aufgebaut, Ihre eigene USt-Nummer steht statisch im Seller-Block.
  3. HTTP Request-Node macht ein POST /api/invoices/generate mit Idempotency-Key: stripe-{{$json.id}}. Stripe darf denselben Webhook gerne dreimal erneut zustellen; Ihr Archiv erhält genau eine Rechnung.
  4. HTTP Response-Node erhält einen PDF/A-3-Stream und eine Evidence-Pack-URL. Schreiben Sie die PDF in Ihren Retention-Bucket, speichern Sie die Evidence-Pack-URL in Ihrer Buchhaltungsdatenbank.

Das war’s. Kein separater Rendering-Schritt, keine Template-Engine, die Sie pflegen, keine XML, die Sie von Hand bauen. Die Factur-X-XML kommt von unserer Seite der Mauer, konform zu 1.0 BASIC, und passt in jede PEPPOL-Pipeline, die ein Empfänger anfordern kann.

Was es löst

Die drei Änderungen adressieren zusammen ein Problem, nämlich die Lücke zwischen “Ich habe strukturierte Daten” und “Ich habe ein Dokument, das eine belgische oder französische Steuerbehörde als Original akzeptiert”. Vor Sprint 49 war diese Lücke ein Drittanbieter. Seit Sprint 50 ist sie ein HTTP-Request.

Wenn Ihr Flow bereits über n8n-nodes-sealdoc oder die Make-App läuft, kommen die neuen Endpoints im nächsten Node-Release. Wenn Sie direkt mit der REST-API sprechen, sind sie heute live.

Und nebenbei: jährliche Abrechnung

Eine kleinere Änderung, aber genug Kunden haben danach gefragt, dass sie eine Erwähnung verdient. In den Abonnement-Einstellungen können Sie jetzt zwischen Monatlich und Jährlich umschalten. Die Jahrespreise sind PriceMonthly × 10, also zwei Monate gratis, und Mollie zieht alle zwölf Monate ein statt alle dreißig Tage. Keine Migration, keine Vertragsänderung: legen Sie den Schalter zum nächsten Verlängerungszeitpunkt um.

Die Compliance-Plumbing ist gelöst. Was Sie mit der zurückgewonnenen Zeit anfangen, liegt bei Ihnen.


← Back to all articles