LiveZUGFeRD / Factur-X

Ihr Layout, unser XML

Sie haben Ihr Rechnungs-PDF schon — aus dem ERP, aus der Druckerei, aus der eigenen Vorlage. Dieser Endpunkt nimmt es als Träger, erzeugt das CII-XML aus Ihren Rechnungsdaten und gibt genau dieses PDF zurück, mit dem XML als factur-x.xml darin.

POST/api/v1/invoice/{countryCode}/{format}/embed

Der Unterschied zu /generate

Beide Endpunkte erzeugen dieselbe E-Rechnung aus denselben Daten und laufen über dasselbe Entitlement. Der einzige Unterschied ist die Seite, die der Empfänger sieht.

  • • Bei /generate kommt das Sichtblatt aus unserer Vorlage — dann ist die PDF/A-3-Konformität unser Problem.
  • • Bei /embed kommt das Sichtblatt von Ihnen und bleibt unverändert; wir legen nur das XML hinein und setzen die Kennungen.
  • • Gleiche Rechnungsdaten, gleiche Prüfkette, gleiche Fehlerbilder (422 mit denselben Regelcodes).

template und templateId gibt es auf dieser Route nicht. Der Rumpf ist strikt und weist sie mit 400 ab, statt sie still zu ignorieren — das Layout ist ja Ihres.

Zur Creator API (/generate)

Nur für die Zwitterformate

Ein Zwitterdokument braucht ein Sichtblatt, für das ein Träger-PDF einspringen kann. Reine XML-Formate wie xrechnung oder ubl haben keines, und das reine pdf trägt kein XML — beide antworten mit 400.

Pfad-Parameter

FeldTypPflichtBeschreibung
countryCodestringLand des Zwitterformats — de, ch, fr oder be.
formatstringDas Zwitterformat: zugferd für DE und CH, facturx für FR und BE.

Anfrage

FeldTypPflichtBeschreibung
pdfstring (base64)Ihr Träger-PDF, Base64-kodiert. Es wird das Sichtblatt des Zwitterdokuments.
invoiceobjectDie Rechnungsdaten, aus denen das XML erzeugt wird — dieselbe Struktur wie bei /generate, mit denselben Pflichtfeldern.
validateboolean-Führt zusätzlich die XSD- und Schematron-Prüfung aus und gibt das Urteil im Block validation zurück. Kostet eine zweite Einheit, genau wie bei /generate.
formatOptions.profilestring-ZUGFeRD-/Factur-X-Profil, Standard EN16931.
formatOptions.versionstring-Fassung des Formats, Standard 2.4 für ZUGFeRD und 1.08 für Factur-X.

PDF/A-3 wird deklariert, nicht konvertiert

Wir setzen die Kennung an dem Träger, den Sie geschickt haben. Wir schreiben Ihr PDF nicht um. Bei einem beliebigen PDF kann diese Deklaration deshalb falsch sein.

  • • Am häufigsten, wenn Ihr PDF Schriften ohne eingebettete Schriftdatei verwendet — das verlangt PDF/A-3.
  • • Was wir tatsächlich geprüft haben, steht in carrier.checks; was auffiel, in carrier.warnings.
  • • Ein vollständiger PDF/A-3b-Nachweis (veraPDF) findet bei uns nicht statt. Abgelehnt wird deswegen nichts: Die Feststellung ist unsere, die Entscheidung ist Ihre.

Wenn Ihr Empfänger einen vollständigen Nachweis verlangt: Schicken Sie Ihren Träger einmal durch einen PDF/A-Konverter, bevor er zu uns kommt — oder nutzen Sie /generate, wo das PDF unseres ist.

Beispiel

bash
1curl -X POST https://service.invoice-api.xhub.io/api/v1/invoice/de/zugferd/embed \
2 -H "Authorization: Bearer sk_live_abc123..." \
3 -H "Content-Type: application/json" \
4 -d '{
5 "pdf": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9UeXBlL1BhZ2UvTWVkaWFCb3...",
6 "validate": true,
7 "invoice": {
8 "invoiceNumber": "RE-2025-001",
9 "type": "invoice",
10 "issueDate": "2025-01-15",
11 "dueDate": "2025-02-15",
12 "currency": "EUR",
13 "seller": {
14 "name": "Meine Firma GmbH",
15 "street": "Musterstraße 1",
16 "city": "Berlin",
17 "postalCode": "10115",
18 "countryCode": "DE",
19 "vatId": "DE123456789",
20 "bankAccount": {
21 "iban": "DE89370400440532013000",
22 "bic": "COBADEFFXXX"
23 }
24 },
25 "buyer": {
26 "name": "Kunde AG",
27 "street": "Kundenweg 42",
28 "city": "München",
29 "postalCode": "80331",
30 "countryCode": "DE",
31 "vatId": "DE987654321"
32 },
33 "countrySpecific": {
34 "countryCode": "DE",
35 "buyerReference": "BUYER-REF-001"
36 },
37 "items": [
38 {
39 "position": 1,
40 "description": "Beratungsleistung",
41 "quantity": 10,
42 "unit": "HUR",
43 "unitPrice": 150.00,
44 "taxRate": 19,
45 "taxCategoryCode": "S",
46 "netAmount": 1500.00,
47 "taxAmount": 285.00,
48 "grossAmount": 1785.00
49 }
50 ],
51 "subtotal": 1500.00,
52 "total": 1785.00,
53 "taxSummary": [
54 {
55 "taxRate": 19,
56 "netAmount": 1500.00,
57 "taxAmount": 285.00
58 }
59 ]
60 }
61 }'

Antwort

200 OK
json
1{
2 "success": true,
3 "format": "zugferd",
4 "filename": "RE-2025-001.pdf",
5 "mimeType": "application/pdf",
6 "hash": "9f2c4e1b7a0d5836f4b2c9e18a7d3045c6b1e8f29a4d70b3c5e81f6a2d9c4b70",
7 "payloadHash": "3a7f1e9c2b8d4056a1c7e3f9b2d84e6015c9a7f3e1b8d2460c5a9f7e3b1d8460",
8 "data": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9UeXBlL1BhZ2Uv...",
9 "embeddedXml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rsm:CrossIndustryInvoice ...",
10 "carrier": {
11 "pdfA3Declared": true,
12 "checks": [
13 { "code": "PDF-001", "severity": "error", "passed": true },
14 { "code": "XMP-003", "severity": "warning", "passed": true }
15 ],
16 "warnings": [
17 {
18 "code": "CARRIER_FONTS_NOT_EMBEDDED",
19 "message": "The carrier uses 2 fonts without an embedded font file."
20 }
21 ]
22 },
23 "validation": {
24 "valid": true,
25 "status": "passed",
26 "results": [
27 { "format": "zugferd-2.4-en16931", "valid": true, "errors": [] }
28 ]
29 },
30 "appliedFormatOptions": {
31 "version": "2.4",
32 "profile": "EN16931"
33 }
34}
FeldTypBeschreibung
successbooleanOb das Zwitterdokument entstanden ist.
formatstringDas verwendete Ausgabeformat.
filenamestringVorgeschlagener Dateiname.
mimeTypestringMIME-Typ des zurückgegebenen Dokuments.
hashstringSHA-256 der zurückgegebenen Bytes, hexadezimal. Nicht stabil über Aufrufe hinweg, weil das PDF einen Erzeugungszeitstempel trägt — für idempotente Namen payloadHash nehmen.
payloadHashstringSHA-256 über das kanonische JSON Ihrer Rechnungsdaten — der stabile Schlüssel für idempotente Aufrufe. hash wechselt bei jedem Aufruf, weil das PDF einen Zeitstempel trägt.
datastring (base64)Das Zwitterdokument, Base64-kodiert: Ihr Layout mit dem erzeugten XML darin.
embeddedXmlstringDas eingebettete CII-XML im Klartext, zum Nachsehen.
carrierobjectWas wir an dem erzeugten Dokument gemessen haben — ein Befund, keine Behauptung.
validationobjectDas Prüfurteil — nur vorhanden, wenn die Anfrage validate: true gesetzt hat.
appliedFormatOptionsobjectFassung und Profil, in denen das Dokument wirklich geschrieben wurde: Ihre Angaben oder die Vorgaben.

Der carrier-Block

FeldTypBeschreibung
pdfA3Declaredbooleantrue heißt: Wir haben die PDF/A-3-Kennung auf Ihrem Träger gesetzt (XMP pdfaid:part=3, Stufe B, sRGB-OutputIntent). Es heißt nicht, dass Ihr Träger konform ist.
checksarrayDie Einzelprüfungen, je mit code, severity (warning, error, fatal), passed und, wenn es einen gibt, message.
warningsarrayFeststellungen zu Ihrem Träger, die wir melden, aber nicht ablehnen — etwa CARRIER_FONTS_NOT_EMBEDDED.

Der validation-Block

Nur vorhanden, wenn Sie validate: true geschickt haben. status sagt, was passiert ist: passed — die Regelwerke liefen und fanden nichts; failed — sie fanden etwas, valid ist dann false und errors nennt es; not_run — sie liefen nicht vollständig, valid ist null und reason nennt den Grund (missing, load-failed, transform-failed oder not-bound). Ein not_run ist kein Freibrief: Über das Dokument ist damit nichts bewiesen.

Abrechnung

Der Aufruf läuft auf dem vorhandenen create-Entitlement des Formats und kostet eine Einheit — zwei, wenn Sie validate: true setzen. Kein neues Entitlement, kein Zuschlag.

Fehler

StatusCodeBeschreibung
400Bad RequestDas Land-Format-Paar ist kein Zwitterformat — ein reines XML-Format oder pdf hat kein Sichtblatt.
400Bad Requesttemplate oder templateId wurde geschickt. Auf dieser Route gibt es beides nicht.
400INVALID_PDFDer Träger ist kein lesbares PDF.
401UNAUTHORIZEDAPI-Key fehlt oder ist ungültig.
403FORBIDDENDer Key hält das create-Entitlement des Formats nicht.
413PAYLOAD_TOO_LARGEDas Träger-PDF ist größer als das konfigurierte Limit.
422UNPROCESSABLEAus den Rechnungsdaten lässt sich keine konforme E-Rechnung bauen — derselbe Rumpf wie bei /generate, mit errors und complianceErrors und ohne data.
429QUOTA_EXCEEDEDDas Kontingent des Formats ist erschöpft.