E-Rechnungen (ZUGFeRD, Factur-X)
Eine E-Rechnung ist das PDF, das Ihre Vorlage rendert, und dieselbe Rechnung als XML, in einer Datei. Formfeed erzeugt ZUGFeRD- und Factur-X-Rechnungen: Es wandelt das gerenderte PDF in PDF/A-3b um, bettet die Rechnung als XML ein und validiert die Datei, bevor sie ausgeliefert wird. Das XML wird aus einem _invoice-Block in den Render-Daten gebaut.
Formfeed ist ein Werkzeug, das das Format schreibt. Es leistet keine Steuerberatung: Sie stellen die Rechnung aus und stehen für ihren Inhalt und ihre umsatzsteuerliche Behandlung ein, Formfeed für das Format.
Was Sie erhalten
- Eine Datei für Menschen und für Software. Das PDF sieht aus, wie Ihre Vorlage es rendert. Es ist ein PDF/A-3b und trägt die Rechnung als Anhang namens
factur-x.xml, in der Syntax Cross Industry Invoice (CII), die ZUGFeRD und Factur-X verwenden. - ZUGFeRD und Factur-X. Beide sind eine Spezifikation unter zwei Namen, und die Datei ist dieselbe. Formfeed schreibt die Version Factur-X 1.09, das ist ZUGFeRD 2.5, in den Profilen
en16931undbasic. - Das XML kommt aus Ihren Daten. Es wird aus
data._invoicegebaut und nie aus dem PDF herausgelesen. Formfeed berechnet nichts: Eine Summe, die nicht aufgeht, wird abgelehnt, nie gerundet. - Vor der Auslieferung validiert. Das XML wird an seinem Schema und an den Regeln des Profils gemessen, zu denen die Geschäftsregeln der EN 16931 gehören, und die Datei an PDF/A-3b. Eine Datei, die nicht besteht, wird nicht ausgeliefert, und an ihrer Stelle auch kein gewöhnliches PDF: Das Rendering schlägt fehl und sagt, warum.
- Ein Bericht zu jeder Rechnung. Das Feld
einvoicedes Renderings sagt, was erzeugt wurde, was der Validator gefunden hat und ob das PDF die Werte zeigt, die das XML nennt.
Das funktioniert für die PDF-Ausgabe von HTML-Vorlagen, von Word- und PowerPoint-Vorlagen, von Ad-hoc-Renderings mit html und von Renderings mit url.
Abdeckung der E-Rechnung nennt die Geschäftsbegriffe der EN 16931, die die Datei trägt, und den Validator und die Regeln, die sie besteht. E-Rechnungen nach Ländern sagt, welches Land was verlangt und wo der Teil von Formfeed endet.
Schnellstart
- Einen Schlüssel besorgen. Ein Test-Schlüssel (
ff_test_…) erzeugt E-Rechnungen in jedem Plan, jede mit der Testmarkierung, bis zu einer Anzahl pro Tag; ein Live-Schlüssel (ff_live_…) erzeugt sie ab dem Starter-Plan. Siehe Authentifizierung und Pläne und Einheiten. - Eine Rechnungsvorlage anlegen. Am schnellsten geht es mit einer Kopie des Beispiels: Vorlagen → Beispiele → E-Rechnung: ZUGFeRD und Factur-X aus einer Vorlage. Sie legt die E-Rechnung fest und gibt die Werte ihres
_invoice-Blocks aus. Bei einer eigenen Vorlage schalten Sie die E-Rechnung in der Seiteneinrichtung ein (Einschalten) und geben dieselben Werte aus. Veröffentlichen Sie die Version. - Die Rechnungsdaten senden.
POST /v1/rendersmit dem Slug der Vorlage und dem_invoice-Block indata, wie in der Anfrage unten. - Den Bericht lesen. Ein erfolgreiches Rendering hat eine gültige E-Rechnung erzeugt. Sein
einvoice.validation.messagesnennt die Warnungen des Validators,einvoice.display.missingdie Werte des XML, die das PDF nicht zeigt (Was zurückkommt).
Einschalten
Eine E-Rechnung lässt sich an drei Stellen festlegen. Sie werden Feld für Feld zusammengeführt, die spätere hat Vorrang:
- Die Einstellungen der Vorlage (
settings.einvoice). Jedes PDF, das aus der Vorlage gerendert wird, ist dann eine E-Rechnung, und eine Anfrage muss nichts dazu sagen. settings.einvoiceder Anfrage.post.einvoiceder Anfrage: ein Objekt mit den Optionen,{}für die Standardwerte, oderfalse.
Jede Stelle nimmt dasselbe Objekt mit Optionen oder false. false verwirft, was die Stellen davor festgelegt haben; post.einvoice: false schaltet also die Festlegung einer Vorlage für eine Anfrage ab. Ein Wert, der weder ein Objekt mit diesen Optionen noch false ist, beantwortet die API mit 400 invalid_request und nennt das Feld.
In der Vorlage
Öffnen Sie im Editor die Seiteneinrichtung, gehen Sie zum Abschnitt E-Rechnung und schalten Sie Die Rechnung als XML in jedes PDF einbetten ein. Der Abschnitt bietet dann Format, Profil, Wenn das PDF einen Wert des XML nicht zeigt und Das XML als eigene Datei speichern; das sind die Optionen weiter unten.
Darunter sagt die Karte Rechnungsdaten, was der Datensatz auf dem Bildschirm über seinen _invoice-Block zu sagen hat: Vollständig mit Rechnungsnummer, Positionen und Gesamtbetrag, die Zahl der Befunde mit dem Feld und der Regel, die jeder verletzt, oder Kein _invoice-Block, mit _invoice-Block hinzufügen, das einen Block mit Platzhaltern einfügt. Die Statusleiste nennt die Art der E-Rechnung, die die Vorlage erzeugt. Speichern und veröffentlichen Sie die Version, wie bei jeder anderen Einstellung.
In einem Plan ohne E-Rechnungen lässt sich der Schalter nicht einschalten, es sei denn, die Test-Renderings des Plans erzeugen welche (im Free-Plan ist das so, siehe Pläne und Einheiten). Eine Vorlage, die schon eine festlegt, etwa eine Kopie des Beispiels, zeigt den Schalter eingeschaltet, und Sie können ihn ausschalten, um gewöhnliche PDFs zu rendern.
Bildvorlagen haben keinen solchen Abschnitt, denn eine E-Rechnung ist ein PDF. Word- und PowerPoint-Vorlagen haben auf ihrer Seite ebenfalls keinen: Legen Sie die E-Rechnung in ihren Einstellungen über die Vorlagen-API oder die settings.json des Projekts fest, oder verlangen Sie sie pro Anfrage.
In einem Projekt ist die Festlegung der Schlüssel einvoice der settings.json:
{
"einvoice": { "profile": "en16931", "flavour": "zugferd" }
}
In der Anfrage
curl https://api-eu.formfeed.dev/v1/renders \
-H "Authorization: Bearer $FORMFEED_API_KEY" \
-H "Idempotency-Key: invoice-RE-2026-0001" \
-H "Content-Type: application/json" \
-d '{
"template": "invoice",
"data": {
"_invoice": {
"number": "RE-2026-0001",
"issue_date": "2026-01-15",
"type_code": "380",
"currency": "EUR",
"seller": {
"name": "Fennlor Studio GmbH",
"vat_id": "DE000000000",
"address": { "street": "Musterstraße 1", "postcode": "12345", "city": "Musterstadt", "country": "DE" },
"contact": { "name": "Erika Mustermann", "email": "rechnung@fennlor.example", "phone": "+49 30 0000000" }
},
"buyer": {
"name": "Olvarest GmbH",
"address": { "street": "Beispielweg 2", "postcode": "54321", "city": "Beispielstadt", "country": "DE" }
},
"delivery": { "date": "2026-01-15" },
"lines": [
{ "id": "1", "name": "Consulting", "quantity": 8, "unit_code": "HUR", "net_price": 120, "net_amount": 960, "tax": { "category": "S", "rate": 19 } }
],
"tax": { "breakdown": [{ "category": "S", "rate": 19, "basis": 960, "amount": 182.4 }] },
"totals": { "line_net": 960, "tax_basis": 960, "tax_total": 182.4, "grand": 1142.4, "due": 1142.4 },
"payment": { "means_code": "58", "iban": "DE36000000000000000000", "reference": "RE-2026-0001", "terms": "Zahlbar innerhalb von 14 Tagen ohne Abzug", "due_date": "2026-01-29" }
}
},
"post": { "einvoice": { "profile": "en16931", "flavour": "zugferd" } }
}'
Die Antwort ist das Rendering, mit dem Bericht in einvoice. Bei einer Vorlage, die selbst eine E-Rechnung festlegt, lassen Sie post weg: Der _invoice-Block in data ist alles, was die Anfrage braucht.
-
Asynchrone Renderings und Batches nehmen dieselben Felder. Ein Batch reiht nichts ein, solange nicht jeder Eintrag, der eine E-Rechnung erzeugt, auch eine erzeugen kann; das Problem nennt den ersten Eintrag, der es nicht kann, in
index. -
post.einvoice: falseschaltet die Festlegung einer Vorlage für eine Anfrage ab, zum Beispiel für ein gewöhnliches PDF mit Passwort:{ "template": "invoice", "data": { … }, "post": { "einvoice": false, "password": { "user": "open-sesame" } } } -
Ad-hoc-
htmlnimmt die Rechnung ebenfalls ausdata._invoice. Ohneenginewird das HTML unverändert gedruckt, unddataenthält nichts außer dem Block:{ "html": "<h1>Rechnung RE-2026-0001</h1> …", "data": { "_invoice": { … } }, "post": { "einvoice": {} } } -
Ein Rendering mit
urlfunktioniert genauso: Die Seite unter der Adresse wird unverändert gedruckt, die Rechnung kommt ausdata._invoice, und die Sichtprüfung liest den Text dieser Seite:{ "url": "https://invoices.example/RE-2026-0001", "data": { "_invoice": { … } }, "post": { "einvoice": {} } }
Optionen
Dieselben vier Optionen sind die Felder von post.einvoice, von settings.einvoice und des Abschnitts E-Rechnung im Editor. Jede hat einen Standardwert, {} verlangt also eine Factur-X-Rechnung im Profil EN 16931.
| Option | Werte | Standard | Bedeutung |
|---|---|---|---|
profile | en16931, basic | en16931 | en16931 trägt jedes Feld der europäischen Norm. basic ist das kleinste Profil, das für sich allein eine Rechnung ist; für eine Rechnung zwischen zwei französischen Unternehmen nimmt Frankreich es nicht an. |
flavour | factur-x, zugferd | factur-x | Der Name, unter dem die Rechnung läuft. ZUGFeRD und Factur-X sind eine Spezifikation unter zwei Namen, und die Datei ist dieselbe: Das eingebettete XML heißt in beiden Fällen factur-x.xml. Die Wahl bestimmt den Namen und die Version, die das Rendering meldet, Factur-X 1.09 oder ZUGFeRD 2.5. |
xml | embedded, both | embedded | both speichert das XML zusätzlich als eigene Datei neben dem PDF. |
display_check | warn, strict | warn | Was passiert, wenn das PDF einen Wert des XML nicht zeigt: warn meldet es, strict lässt das Rendering fehlschlagen. Siehe Die Sichtprüfung. |
- Groß- und Kleinschreibung und Leerzeichen am Rand spielen bei
profileundflavourkeine Rolle. Ein Profil oder einflavouraußerhalb dieser Werte beantwortet die API mit422 einvoice_profile_unsupported; dazu gehörenminimum,basic-wl,extendedundxrechnung. - Eine Option, die es nicht gibt, oder ein Wert von
xmloderdisplay_checkaußerhalb seiner Liste beantwortet die API mit400 invalid_request. - In
basiclässt das XML weg, wofür dieses Profil keinen Platz hat: dencontactvon Verkäufer und Käufer,seller.legal_info, bei einer Positiondescription,seller_item_id,buyer_item_id,order_line_referenceundbuyer_accounting_referencesowiepayment.bicundpayment.account_name. Eintotals.roundingungleich 0 wird inbasicabgelehnt. Im Profil basic ausgelassen nennt sie mit ihren Geschäftsbegriffen. - Eine Rechnung zwischen zwei französischen Unternehmen wird in
basicmit422 einvoice_data_invalidabgelehnt, weil Frankreichs Reform nur die Profile EN 16931 und EXTENDED-CTC-FR annimmt. Siehe Französische Inlandsrechnungen. - Die Ebenen werden Feld für Feld zusammengeführt. Eine Vorlage, die
{ "flavour": "zugferd", "display_check": "strict" }festlegt, und eine Anfrage mit"post": { "einvoice": { "xml": "both" } }ergeben eine ZUGFeRD-Rechnung im Profil EN 16931, mit strenger Sichtprüfung und dem XML neben dem PDF.
Der _invoice-Block
_invoice steht auf der obersten Ebene von data, neben allem, was die Vorlage sonst verwendet. Dies ist der Block der Anfrage oben; denselben fügt _invoice-Block hinzufügen im Editor ein (dort mit dem heutigen Datum als issue_date und dem Fälligkeitsdatum 14 Tage später):
{
"_invoice": {
"number": "RE-2026-0001",
"issue_date": "2026-01-15",
"type_code": "380",
"currency": "EUR",
"seller": {
"name": "Fennlor Studio GmbH",
"vat_id": "DE000000000",
"address": {
"street": "Musterstraße 1",
"postcode": "12345",
"city": "Musterstadt",
"country": "DE"
},
"contact": {
"name": "Erika Mustermann",
"email": "rechnung@fennlor.example",
"phone": "+49 30 0000000"
}
},
"buyer": {
"name": "Olvarest GmbH",
"address": {
"street": "Beispielweg 2",
"postcode": "54321",
"city": "Beispielstadt",
"country": "DE"
}
},
"delivery": { "date": "2026-01-15" },
"lines": [
{
"id": "1",
"name": "Consulting",
"quantity": 8,
"unit_code": "HUR",
"net_price": 120,
"net_amount": 960,
"tax": { "category": "S", "rate": 19 }
}
],
"tax": {
"breakdown": [
{ "category": "S", "rate": 19, "basis": 960, "amount": 182.4 }
]
},
"totals": {
"line_net": 960,
"tax_basis": 960,
"tax_total": 182.4,
"grand": 1142.4,
"due": 1142.4
},
"payment": {
"means_code": "58",
"iban": "DE36000000000000000000",
"reference": "RE-2026-0001",
"terms": "Zahlbar innerhalb von 14 Tagen ohne Abzug",
"due_date": "2026-01-29"
}
}
}
Was für jedes Feld gilt:
- Zahlen sind JSON-Zahlen oder dasselbe als Text (
1142.4oder"1142.40"). Ein Geldbetrag hat höchstens 2 Nachkommastellen, eine Menge oder ein Einzelpreis höchstens 6, ein Satz oder ein Prozentwert höchstens 4. Mehr Nachkommastellen sind ein Befund; es wird nichts für Sie gerundet. Eine JSON-Zahl wird mit fünfzehn signifikanten Stellen gelesen,0.30000000000000004zählt also als0.3; eine Zahl als Text wird genommen, wie sie geschrieben ist. - Textfelder dürfen nicht leer sein und nicht nur aus Leerzeichen bestehen. Lassen Sie das Feld dann weg.
- Datumsangaben haben die Form
YYYY-MM-DDund müssen im Kalender existieren. - Codes (
currency,address.country,unit_code,payment.means_code,type_code) müssen in ihrer Codeliste stehen, in der Fassung, die der Validator verwendet; Groß- und Kleinschreibung spielen keine Rolle. - Kennungen (
id,legal_registration,electronic_address,standard_item_id) sind eine Zeichenkette oder ein Objekt{ "value": "…", "scheme": "…" }, das das Schema nennt, zu dem der Wert gehört. - Unbekannte Felder werden verworfen, nicht abgelehnt. Die Vorlage liest
_invoiceauch für die Anzeige; Daten, die nur die Seite braucht, dürfen also im Block stehen.
Die Tabellen nennen den Geschäftsbegriff (BT) oder die Gruppe (BG) der EN 16931, die ein Feld trägt. Abdeckung der E-Rechnung führt alle Begriffe in der umgekehrten Richtung auf, und die, die der Block nicht trägt.
Wie der Block geprüft wird
Bevor etwas gerendert oder gezählt wird, wird der Block an den Regeln der Tabellen unten gemessen, in drei Stufen:
- Die Form: Jedes Pflichtfeld ist da, mit dem richtigen Typ.
- Die Werte: Nachkommastellen, Datumsangaben, Codes, die Prüfziffern einer IBAN, das Länderkürzel am Anfang einer USt-IdNr.
- Die Regeln der EN 16931: Die Summen gehen auf, die Umsatzsteueraufschlüsselung passt zu den Positionen, jede Umsatzsteuerkategorie hat, was sie verlangt, die Zahlungsangaben sind vollständig, und bei einer Rechnung zwischen zwei französischen Unternehmen ist da, was Frankreich verlangt.
Eine Stufe wird erst geprüft, wenn die vorige bestanden hat; einem Block, dem ein Feld fehlt, wird also noch nicht gesagt, dass seine Summen nicht aufgehen. Einen Block, der nicht besteht, beantwortet die API mit 422 einvoice_data_invalid. errors nennt jeden Befund der Stufe, jeden mit dem Feld (path, ab data._invoice, Listeneinträge ab 0 gezählt), seiner message und, wo eine gilt, der rule; detail wiederholt den ersten:
{
"type": "https://docs.formfeed.dev/errors/einvoice-data-invalid",
"title": "E-invoice data invalid",
"status": 422,
"code": "einvoice_data_invalid",
"detail": "_invoice.totals.due is 1000.00, but grand less prepaid plus rounding is 1142.40 (BR-CO-16)",
"errors": [
{
"path": "data._invoice.totals.due",
"message": "_invoice.totals.due is 1000.00, but grand less prepaid plus rounding is 1142.40 (BR-CO-16)",
"rule": "BR-CO-16"
}
]
}
Die Karte Rechnungsdaten im Editor, formfeed validate und formfeed einvoice führen dieselbe Prüfung offline aus. Nach dem Rendern misst der Validator die fertige Datei am vollständigen Regelwerk des Profils, das mehr finden kann; siehe Was zurückkommt.
Dokument
| Feld | Begriff | Pflicht | Bedeutung |
|---|---|---|---|
number | BT-1 | ja | Die Rechnungsnummer. |
issue_date | BT-2 | ja | Das Rechnungsdatum. |
type_code | BT-3 | nein | Die Art des Dokuments, ein Code aus UNTDID 1001, als Text oder als Zahl: 380 Rechnung (der Standard), 381 Gutschrift (credit note), 384 korrigierte Rechnung, 389 selbst ausgestellte Rechnung (self-billed invoice). |
currency | BT-5 | ja | Die Währung der Rechnung, ISO 4217 (EUR). |
accounting_currency | BT-6 | nein | Die Währung, in der die Umsatzsteuer gebucht wird, wenn die Rechnung auf eine andere lautet. Gehört zu totals.tax_total_accounting: Geben Sie beide an oder keines (BR-53). |
buyer_reference | BT-10 | nein | Die Leitweg-Angabe des Käufers. Die Leitweg-ID einer deutschen Behörde steht hier. |
order_reference | BT-13 | nein | Die Nummer der Bestellung des Käufers. |
contract_reference | BT-12 | nein | Die Nummer des Vertrags. |
note | BT-22 | nein | Eine Bemerkung zur Rechnung, als freier Text. Sie wird die erste Bemerkung, ohne Betreffcode. |
notes[] | BT-21, BT-22 | je nachdem | Weitere Bemerkungen, jede mit text und optional subject_code (UNTDID 4451). Drei davon sind Pflicht bei französischen Inlandsrechnungen. |
period | BG-14 (BT-73, BT-74) | nein | Der Abrechnungszeitraum: start, end oder beides. Er darf nicht vor seinem Beginn enden (BR-29). |
preceding_invoices[] | BG-3 | nein | Die Rechnungen, auf die sich eine Gutschrift oder eine Korrektur bezieht: number (BT-25, in jedem Eintrag Pflicht) und issue_date (BT-26). |
operation, operation_code | BT-23 | je nachdem | Pflicht bei französischen Inlandsrechnungen. |
Verkäufer und Käufer
seller und buyer nehmen dieselben Felder; wo zwei Begriffe genannt sind, ist der erste der des Verkäufers.
| Feld | Begriff | Pflicht | Bedeutung |
|---|---|---|---|
name | BT-27, BT-44 | ja | Der Name des Unternehmens. |
trading_name | BT-28, BT-45 | nein | Der Name, unter dem das Unternehmen auftritt, wenn er abweicht. |
id | BT-29, BT-46 | nein | Eine Kennung der Partei. |
legal_registration | BT-30, BT-47 | nein | Der Registereintrag: eine Handelsregisternummer, eine SIREN. |
vat_id | BT-31, BT-48 | je nachdem | Die Umsatzsteuer-Identifikationsnummer. Sie beginnt mit ihrem Ländercode, wie in DE000000000 (BR-CO-09; EL für Griechenland); Leerzeichen werden entfernt. Ob sie Pflicht ist, hängt von den Umsatzsteuerkategorien ab. |
address.country | BT-40, BT-55 | ja | Das Land, ISO 3166-1 mit zwei Buchstaben (DE). |
address.street, .street2, .street3 | BT-35, BT-36, BT-162; BT-50, BT-51, BT-163 | nein | Die Zeilen der Straßenanschrift. |
address.postcode, address.city | BT-38, BT-37; BT-53, BT-52 | nein | Die Postleitzahl und der Ort. |
address.subdivision | BT-39, BT-54 | nein | Die Region oder Provinz. |
contact.name, .phone, .email | BG-6, BG-9 | nein | Wen man zur Rechnung fragt: ein Name, der einer Person oder einer Abteilung. In basic ausgelassen. |
electronic_address | BT-34, BT-49 | je nachdem | Wo die Partei elektronische Rechnungen empfängt. Eine E-Mail-Adresse als Zeichenkette (sie erhält das Schema EM), oder ein Objekt, dessen scheme ein Code der EAS-Liste ist, wie in { "value": "000000000", "scheme": "0225" }. Jede andere Zeichenkette wird abgelehnt, weil ihr Schema fehlt. Pflicht für beide Parteien einer französischen Inlandsrechnung. |
Nur der Verkäufer hat:
| Feld | Begriff | Pflicht | Bedeutung |
|---|---|---|---|
seller.tax_number | BT-32 | je nachdem | Die Steuernummer des Verkäufers, wenn es keine USt-IdNr. gibt. Ob sie Pflicht ist, hängt von den Umsatzsteuerkategorien ab. |
seller.legal_info | BT-33 | nein | Weitere rechtliche Angaben, die die Rechnung nennen muss: Registergericht, Kapital. In basic ausgelassen. |
seller.vat_on_debits | BT-8 | je nachdem | Pflicht bei französischen Inlandsrechnungen. |
Nur der Käufer hat buyer.siren (BT-47), siehe französische Inlandsrechnungen.
Der Verkäufer braucht mindestens eines von vat_id, legal_registration und id (BR-CO-26). Welche Steuerkennungen die Prüfung darüber hinaus verlangt, hängt von den Umsatzsteuerkategorien ab, die die Rechnung verwendet.
Lieferung
| Feld | Begriff | Pflicht | Bedeutung |
|---|---|---|---|
delivery.date | BT-72 | nein | Das Datum der Lieferung. |
delivery.name | BT-70 | nein | Der Name der Partei, an die geliefert wurde. |
delivery.address | BG-15 | nein | Die Lieferanschrift, mit den Feldern der Anschrift einer Partei (BT-75 bis BT-80, BT-165). country (BT-80) ist Pflicht, sobald es eine Anschrift gibt. |
Eine Rechnung ohne delivery ist gültig, und ihr Bericht trägt eine Warnung, PEPPOL-EN16931-R008 (ein leeres Element): Die Syntax verlangt das Lieferelement, und es ist leer, wenn der Block keine Lieferung nennt.
Positionen
lines[] (BG-25) hat mindestens einen Eintrag.
| Feld | Begriff | Pflicht | Bedeutung |
|---|---|---|---|
id | BT-126 | ja | Die Kennung der Position, als Text oder als Zahl. |
name | BT-153 | ja | Der Name des Artikels. |
description | BT-154 | nein | Eine längere Beschreibung des Artikels. In basic ausgelassen. |
quantity | BT-129 | ja | Die berechnete Menge, höchstens 6 Nachkommastellen. Eine Gutschrift ist eine negative Menge. |
unit_code | BT-130, BT-150 | nein | Die Einheit, ein Code der UN/ECE-Empfehlung 20 oder 21: C62 ein Stück (der Standard), HUR eine Stunde, DAY ein Tag, KGM ein Kilogramm. Ein Wort wie hours wird abgelehnt (BR-CL-23); bei den gängigen nennt der Befund den gemeinten Code. |
net_price | BT-146 | ja | Der Nettopreis einer Einheit, höchstens 6 Nachkommastellen. Er darf nicht negativ sein (BR-27). |
price_base_quantity | BT-149 | nein | Die Menge, für die der Preis gilt, wenn es nicht eine Einheit ist. |
net_amount | BT-131 | ja | Der Nettobetrag der Position: Menge mal Preis (geteilt durch price_base_quantity, wo es eine gibt), abzüglich der Nachlässe der Position, zuzüglich ihrer Zuschläge. Die Prüfung vor dem Rendern rechnet ihn nicht nach; die Summen werden aus ihm gebildet. |
tax.category | BT-151 | ja | Die Umsatzsteuerkategorie: S, Z, E, AE, K, G oder O. |
tax.rate | BT-152 | je nachdem | Der Umsatzsteuersatz in Prozent, 19 für 19 %. Pflicht für jede Kategorie außer O. |
period | BT-134, BT-135 | nein | Der Zeitraum, den die Position abdeckt: start, end oder beides (BR-30). |
allowances[], charges[] | BG-27, BG-28 | nein | Nachlässe und Zuschläge der Position: amount (in jedem Eintrag Pflicht), reason, reason_code, base_amount, percent, wie beim Dokument. |
buyer_accounting_reference | BT-133 | nein | Die Buchungsreferenz des Käufers für die Position. In basic ausgelassen. |
note | BT-127 | nein | Eine Bemerkung zur Position. |
seller_item_id | BT-155 | nein | Die Artikelnummer des Verkäufers. In basic ausgelassen. |
buyer_item_id | BT-156 | nein | Die Artikelnummer des Käufers. In basic ausgelassen. |
standard_item_id | BT-157 | nein | Eine standardisierte Kennung des Artikels, als Objekt mit ihrem scheme (BR-64): { "value": "…", "scheme": "0160" } für eine GTIN. |
order_line_reference | BT-132 | nein | Die Position der Bestellung des Käufers, auf die diese Position antwortet. In basic ausgelassen. |
Nachlässe und Zuschläge des Dokuments
allowances[] und charges[] auf der obersten Ebene des Blocks (BG-20, BG-21) gelten für die ganze Rechnung. Die Nachlässe und Zuschläge einer Position nehmen dieselben Felder ohne tax.
| Feld | Begriff | Pflicht | Bedeutung |
|---|---|---|---|
amount | BT-92, BT-99 | ja | Der Betrag des Nachlasses oder Zuschlags, ohne Umsatzsteuer. |
tax.category | BT-95, BT-102 | ja | Die Umsatzsteuerkategorie, unter die der Nachlass oder Zuschlag fällt, wie bei einer Position. Nicht bei den Nachlässen und Zuschlägen einer Position, die unter die der Position fallen. |
tax.rate | BT-96, BT-103 | je nachdem | Der Umsatzsteuersatz, wie bei einer Position: Pflicht für jede Kategorie außer O. |
reason | BT-97, BT-104 | je nachdem | Der Grund, als Text. Die EN 16931 verlangt bei jedem Nachlass und Zuschlag reason oder reason_code; die Prüfung vor dem Rendern verlangt es nicht, der Validator schon. |
reason_code | BT-98, BT-105 | je nachdem | Der Grund als Code: UNTDID 5189 für einen Nachlass (95 ein Rabatt), UNTDID 7161 für einen Zuschlag (FC Fracht). |
base_amount, percent | BT-93, BT-94; BT-100, BT-101 | nein | Der Betrag, von dem der Nachlass oder Zuschlag ein Prozentsatz ist, und dieser Prozentsatz. |
Wie die Beträge zusammenpassen, mit einem Nachlass einer Position, einem Nachlass und einem Zuschlag des Dokuments, alle zu 19 %:
{
"lines": [
{
"id": "1",
"name": "Etiketten 100 × 150 mm",
"quantity": 10,
"unit_code": "XRO",
"net_price": 18,
"net_amount": 162,
"tax": { "category": "S", "rate": 19 },
"allowances": [
{
"amount": 18,
"base_amount": 180,
"percent": 10,
"reason": "Mengenrabatt",
"reason_code": "95"
}
]
},
{
"id": "2",
"name": "Thermotransferband",
"quantity": 4,
"net_price": 12.5,
"net_amount": 50,
"tax": { "category": "S", "rate": 19 }
}
],
"allowances": [
{
"amount": 10.6,
"base_amount": 212,
"percent": 5,
"reason": "Treuerabatt",
"reason_code": "95",
"tax": { "category": "S", "rate": 19 }
}
],
"charges": [
{
"amount": 9.9,
"reason": "Versand",
"reason_code": "FC",
"tax": { "category": "S", "rate": 19 }
}
],
"tax": {
"breakdown": [
{ "category": "S", "rate": 19, "basis": 211.3, "amount": 40.15 }
]
},
"totals": {
"line_net": 212,
"allowances": 10.6,
"charges": 9.9,
"tax_basis": 211.3,
"tax_total": 40.15,
"grand": 251.45,
"prepaid": 50,
"due": 201.45
}
}
Der Nachlass der Position steckt im net_amount der ersten Position (10 × 18 − 18 = 162). Nachlass und Zuschlag des Dokuments nicht: Sie gehen in die basis der Aufschlüsselung und in die Summen ein (212 − 10,60 + 9,90 = 211,30).
Umsatzsteueraufschlüsselung
tax.breakdown[] (BG-23) hat einen Eintrag für jede Kombination aus Kategorie und Satz, die die Positionen, Nachlässe und Zuschläge verwenden, und keinen Eintrag doppelt.
| Feld | Begriff | Pflicht | Bedeutung |
|---|---|---|---|
category | BT-118 | ja | Die Umsatzsteuerkategorie. |
rate | BT-119 | je nachdem | Der Umsatzsteuersatz in Prozent, wie bei einer Position. |
basis | BT-116 | ja | Der Betrag, auf den die Umsatzsteuer berechnet wird: die net_amount der Positionen dieser Kategorie und dieses Satzes, zuzüglich der Zuschläge und abzüglich der Nachlässe des Dokuments in dieser Kategorie und zu diesem Satz. |
amount | BT-117 | ja | Der Umsatzsteuerbetrag. Weicht er von Satz mal Basis um mehr ab, als sich durch Runden je Position erklären lässt (ein halber Cent je Position, Nachlass und Zuschlag zu diesem Satz), wird der Block abgelehnt (BR-CO-17). |
exemption_reason | BT-120 | je nachdem | Warum keine Umsatzsteuer berechnet wird, als Text. Eines der beiden Felder für den Grund ist Pflicht für jede Kategorie außer S und Z, die keines nehmen. |
exemption_reason_code | BT-121 | je nachdem | Dasselbe als VATEX-Code, etwa VATEX-EU-AE. |
Umsatzsteuerkategorien
Die Kategorien sind die Codes aus UNCL 5305, wie die EN 16931 sie verwendet. Welche Kategorie und welcher Satz für einen Umsatz gelten, ist eine steuerliche Frage. Formfeed beantwortet sie nicht und nimmt die Werte, die Sie senden. Es prüft nur, ob der Block zu der Kategorie passt, die Sie angeben:
| Kategorie | rate | In der Aufschlüsselung | Verkäufer und Käufer |
|---|---|---|---|
S | über 0 | kein Befreiungsgrund | Verkäufer: vat_id oder tax_number |
Z | 0 | Betrag 0, kein Befreiungsgrund | Verkäufer: vat_id oder tax_number |
E | 0 | Betrag 0, exemption_reason oder exemption_reason_code | Verkäufer: vat_id oder tax_number |
AE | 0 | Betrag 0, exemption_reason oder exemption_reason_code | Verkäufer: vat_id oder tax_number; Käufer: vat_id oder legal_registration |
K | 0 | Betrag 0, exemption_reason oder exemption_reason_code | Verkäufer: vat_id; Käufer: vat_id. Die Rechnung nennt außerdem delivery.date oder period sowie delivery.address. |
G | 0 | Betrag 0, exemption_reason oder exemption_reason_code | Verkäufer: vat_id |
O | entfällt | Betrag 0, exemption_reason oder exemption_reason_code; keine andere Kategorie in der Rechnung | weder Verkäufer noch Käufer nennen eine vat_id |
Die Regeln, die ein Befund nennt, folgen der Kategorie: BR-S-…, BR-Z-…, BR-E-…, BR-AE-…, BR-IC-… für K, BR-G-… und BR-O-….
Summen
totals (BG-22). Jede Summe muss auf den Cent dem entsprechen, als was die Norm sie definiert. allowances, charges, prepaid und rounding zählen als 0, wenn sie fehlen, und werden trotzdem geprüft: Eine Rechnung mit Nachlässen des Dokuments muss totals.allowances angeben.
| Feld | Begriff | Pflicht | Bedeutung |
|---|---|---|---|
line_net | BT-106 | ja | Die Summe der net_amount der Positionen (BR-CO-10). |
allowances | BT-107 | nein | Die Summe der Nachlässe des Dokuments (BR-CO-11). |
charges | BT-108 | nein | Die Summe der Zuschläge des Dokuments (BR-CO-12). |
tax_basis | BT-109 | ja | Die Nettosumme: line_net abzüglich allowances zuzüglich charges (BR-CO-13). |
tax_total | BT-110 | ja | Die Umsatzsteuer: die Summe der Beträge der Aufschlüsselung (BR-CO-14). |
tax_total_accounting | BT-111 | nein | Die Umsatzsteuer in der accounting_currency (BR-53). |
grand | BT-112 | ja | Der Gesamtbetrag mit Umsatzsteuer: tax_basis zuzüglich tax_total (BR-CO-15). |
prepaid | BT-113 | nein | Was bereits gezahlt wurde. |
rounding | BT-114 | nein | Ein Rundungsbetrag. In basic abgelehnt, wenn er nicht 0 ist. |
due | BT-115 | ja | Der Zahlbetrag: grand abzüglich prepaid zuzüglich rounding (BR-CO-16). |
Zahlung
payment ist als Ganzes optional, aber eine Rechnung mit einem Zahlbetrag über 0 nennt due_date oder terms (BR-CO-25).
| Feld | Begriff | Pflicht | Bedeutung |
|---|---|---|---|
means_code | BT-81 | je nachdem | Wie die Rechnung bezahlt wird, ein Code aus UNTDID 4461, als Text oder als Zahl: 58 SEPA-Überweisung, 59 SEPA-Lastschrift, 30 Überweisung, 48 Karte. Pflicht, sobald iban, bic, account_name, mandate_id oder debited_iban angegeben ist (BR-49). |
iban | BT-84 | je nachdem | Das Konto, auf das zu zahlen ist. Pflicht bei den Codes 30 und 58 (BR-61). Die Prüfziffern werden geprüft. |
bic | BT-86 | nein | Die BIC dieses Kontos. In basic ausgelassen. |
account_name | BT-85 | nein | Der Name des Kontos. In basic ausgelassen. |
reference | BT-83 | nein | Was der Zahlende bei der Zahlung angibt. |
terms | BT-20 | je nachdem | Die Zahlungsbedingungen, als Text. Eines von terms und due_date ist Pflicht, wenn totals.due über 0 liegt. |
due_date | BT-9 | je nachdem | Das Datum, an dem die Zahlung fällig ist. |
mandate_id | BT-89 | nein | Lastschrift: das Mandat. |
creditor_id | BT-90 | nein | Lastschrift: die Gläubiger-Identifikation. |
debited_iban | BT-91 | nein | Lastschrift: das Konto, das belastet wird. Die Prüfziffern werden geprüft. |
Dieselben Werte in der Vorlage ausgeben
Das XML ist die Rechnung, das PDF muss also zeigen, was darin steht. Der verlässliche Weg ist, in der Vorlage die Werte aus _invoice auszugeben, statt Werte aus anderen Feldern oder Summen, die die Vorlage selbst addiert:
<h1>Rechnung {{ _invoice.number }}</h1>
<p>{{ _invoice.buyer.name }}</p>
<table>
<thead>
<tr>
<th>Leistung</th>
<th>Menge</th>
<th>Preis</th>
<th>Netto</th>
</tr>
</thead>
<tbody>
{% for line in _invoice.lines %}
<tr>
<td>{{ line.name }}</td>
<td>{{ line.quantity }}</td>
<td>{{ line.net_price | money }}</td>
<td>{{ line.net_amount | money }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>Netto {{ _invoice.totals.tax_basis | money }}</p>
<p>Umsatzsteuer {{ _invoice.totals.tax_total | money }}</p>
<p>Gesamt {{ _invoice.totals.grand | money }}</p>
<p>Zahlbetrag {{ _invoice.totals.due | money }}</p>
Die Helper formatieren die Werte für den Leser; die Sichtprüfung liest Beträge in beiden Schreibweisen. Das XML hängt weder von der locale noch von Übersetzungen ab: Ein deutsches und ein englisches PDF aus denselben Daten betten dasselbe XML ein.
Das Beispiel E-Rechnung: ZUGFeRD und Factur-X aus einer Vorlage ist eine vollständige Rechnungsvorlage, die so gebaut ist. Vorlagen → Beispiele in der App legt eine Kopie davon in Ihrem Workspace an.
Die Sichtprüfung
In einer E-Rechnung ist das XML die Rechnung. Nichts zwingt eine Vorlage, die Werte aus _invoice auszugeben: Eine, die ihre eigenen Summen addiert oder die Zahl eines anderen Felds zeigt, rendert ohne Beanstandung, und ein PDF, das andere Rechnungsangaben zeigt, kann als zweite Rechnung gelten. Deshalb liest Formfeed den Text des fertigen PDFs, einschließlich Kopf- und Fußzeilen, und sucht nach dem, was das XML nennt:
| Begriff | Wert | Gesucht |
|---|---|---|
| BT-1 | number | als Text; Leerzeichen, Zeilenumbrüche, Groß- und Kleinschreibung und die Art des Strichs spielen keine Rolle |
| BT-109 | totals.tax_basis | als Zahl |
| BT-110 | totals.tax_total | als Zahl |
| BT-112 | totals.grand | als Zahl |
| BT-115 | totals.due | als Zahl, wenn er von totals.grand abweicht |
| BT-117 | jedes tax.breakdown[].amount | als Zahl, wenn die Aufschlüsselung mehr als einen Eintrag hat |
- Eine Zahl gilt in beiden Schreibweisen als gezeigt,
1.234,56oder1,234.56. Tausender dürfen auch mit einem Leerzeichen oder einem Apostroph gruppiert sein, und das Vorzeichen wird ignoriert, weil eine Gutschrift negativ ausgeben kann, was ihr XML positiv nennt. - Nach Nullbeträgen wird nicht gesucht: Eine Vorlage muss
0,00nicht ausgeben. Datumsangaben werden nicht verglichen. - Das Ergebnis ist
einvoice.display:checkednennt die Begriffe, nach denen gesucht wurde,missingdie, die der Text nicht zeigt.
Was ein fehlender Wert bewirkt, bestimmt die Option display_check:
warn, der Standard: Das Rendering gelingt, unddisplay.missingnennt die Begriffe.strict: Das Rendering schlägt mit422 einvoice_display_mismatchfehl, und es wird keine Datei ausgeliefert. Das Problem trägt den Bericht ineinvoice.
Die Prüfung findet einen Wert, der fehlt, nicht einen falschen, der neben dem richtigen steht. Sie ist ein Netz, kein Beweis: Eine Zahl gilt als gezeigt, wo immer sie im Text steht, und ein Wert, der Teil eines Bilds ist, wird nicht gefunden, weil die Prüfung Text liest.
Der Editor führt dieselbe Prüfung aus, bevor etwas gerendert wird. Bei einer Vorlage, die eine E-Rechnung festlegt, zählen die Vorschauen Fließend und Seiten die Rechnungswerte, die das Dokument zeigt, und die Liste Probleme nennt jeden, der fehlt. formfeed render gibt das Ergebnis der Prüfung des Renderings selbst aus.
Französische Inlandsrechnungen
Ist address.country des Verkäufers und des Käufers jeweils FR, verlangt die Prüfung, was Frankreichs Regeln über die EN 16931 hinaus zur Pflicht machen:
| Feld | Regel | Bedeutung |
|---|---|---|
seller.legal_registration | BR-FR-10 | Die SIREN des Verkäufers (BT-30), neun Ziffern. |
buyer.siren | BR-FR-11 | Die SIREN des Käufers, neun Ziffern. Sie wird als Registereintrag des Käufers (BT-47) mit dem Schema 0002 geschrieben. Eine SIREN in buyer.legal_registration zählt ebenfalls; sind beide angegeben, müssen sie übereinstimmen. |
operation | BR-FR-08 | Was verkauft wurde: goods, services oder mixed. Daraus wird der Abrechnungsrahmen B1, S1 oder M1 im Geschäftsprozess (BT-23). Für einen anderen Rahmen senden Sie seinen Code als operation_code, der Vorrang vor operation hat: B1, S1, M1, B2, S2, M2, S3, B4, S4, M4, S5, S6, B7, S7, B8, S8 oder M8. |
seller.vat_on_debits | true, wenn der Verkäufer die Umsatzsteuer mit der Rechnungsstellung schuldet, false, wenn sie mit der Zahlung geschuldet wird. true schreibt den Code für „Datum der Rechnung“ (BT-8) in jede Umsatzsteueraufschlüsselung; false schreibt nichts. | |
notes[] | BR-FR-05 | Die gesetzlichen Hinweise, jeder als eigene Bemerkung mit seinem subject_code (BT-21): PMT für die Pauschale von 40 € für Beitreibungskosten, PMD für die Verzugsstrafen und AAB für das Skonto bei vorzeitiger Zahlung oder dass keines gewährt wird. |
seller.electronic_address, buyer.electronic_address | BR-FR-13, BR-FR-12 | Die elektronischen Adressen von Verkäufer (BT-34) und Käufer (BT-49), jede mit ihrem Schema. |
Das sind die Felder, die Sie im Block oben ergänzen oder ersetzen. Mit ihnen und mit französischen Anschriften beider Parteien besteht der Block die Prüfung in en16931:
{
"operation": "services",
"notes": [
{
"subject_code": "PMT",
"text": "En cas de retard de paiement, une indemnité forfaitaire pour frais de recouvrement de 40 € sera exigée."
},
{
"subject_code": "PMD",
"text": "En cas de retard de paiement, des pénalités seront appliquées au taux de trois fois le taux d'intérêt légal."
},
{
"subject_code": "AAB",
"text": "Aucun escompte n'est accordé pour paiement anticipé."
}
],
"seller": {
"name": "Fennlor Studio SAS",
"legal_registration": { "value": "000000000", "scheme": "0002" },
"vat_id": "FR00000000000",
"vat_on_debits": true,
"address": {
"street": "1 rue du Modèle",
"postcode": "99000",
"city": "Villemodèle",
"country": "FR"
},
"electronic_address": { "value": "000000000", "scheme": "0225" }
},
"buyer": {
"name": "Olvarest SARL",
"siren": "000000018",
"address": {
"street": "2 avenue de l'Exemple",
"postcode": "99100",
"city": "Exempleville",
"country": "FR"
},
"electronic_address": { "value": "000000018", "scheme": "0225" }
}
}
- Schreiben Sie eine solche Rechnung im Profil
en16931. Frankreichs Reform nimmt nur die Profile EN 16931 und EXTENDED-CTC-FR an, deshalb wirdbasicfür sie mit422 einvoice_data_invalidabgelehnt. - Der Validator prüft die Datei danach gegen Frankreichs Geschäftsregeln als Ganzes, die
BR-FR-Regeln der Norm AFNOR XP Z12-012, in der Fassung, die Mustang 2.26.0 mitbringt (1.3.0 vom 16. Februar 2026). Was gegen sie verstößt, meldet er als Warnung ineinvoice.validation.messagesund liefert die Datei trotzdem aus; deshalb verlangt die Prüfung die Pflichtangaben oben schon vor dem Rendering. Sehen Sie dort nach weiterenBR-FR-Befunden, bevor die Rechnung an Ihre Plattform geht. - Die Lieferanschrift steht, wenn sie von der des Käufers abweicht, in
delivery.address, mitdelivery.nameunddelivery.date. Die Prüfung verlangt sie nicht. - Nichts davon wird verlangt, wenn nur einer der beiden in Frankreich sitzt.
Formfeed erzeugt die Datei. Sie über eine plateforme agréée zu versenden, ist Ihre Aufgabe oder die Ihrer Plattform, siehe Was Formfeed nicht tut.
Was zurückkommt
Ein Rendering, das eine E-Rechnung erzeugt hat, trägt den Bericht in einvoice. Bei jedem anderen Rendering ist das Feld null. Das Beispiel zeigt die Felder, um die es hier geht; das Rendering-Objekt hat die übrigen.
{
"id": "rnd_7da09ab6cd9946b798c187c2a9350852",
"status": "succeeded",
"output": "pdf",
"page_count": 1,
"units": 2,
"einvoice": {
"profile": "en16931",
"flavour": "factur-x",
"spec_version": "1.09",
"xml_url": null,
"validation": {
"valid": true,
"schematron": "Factur-X 1.09.2 (Mustang 2.26.0)",
"pdfa": "PDF/A-3b",
"messages": [],
"id": "6f2c7e9a-1d4b-4c3e-8a5f-2b7d9e0c1a34",
"versions": {
"mustang": "2.26.0",
"verapdf": "1.30.2",
"pdfbox": "3.0.8"
},
"xml_sha256": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b",
"pdf_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
},
"display": {
"checked": ["BT-1", "BT-109", "BT-110", "BT-112"],
"missing": []
}
}
}
| Feld | Bedeutung |
|---|---|
profile, flavour | Was erzeugt wurde, mit allen Standardwerten. |
spec_version | Die Version der Spezifikation, für die die Datei geschrieben und validiert wurde, unter dem Namen aus flavour: 1.09 für Factur-X, 2.5 für ZUGFeRD. |
xml_url | Ein Link zum XML, wenn das Rendering xml: both verlangt hat und die Datei noch gespeichert ist; sonst null. Er entsteht wie download_url: signiert, oder mit access: "public" der öffentliche Link. Das XML steckt in jedem Fall im PDF. |
validation | Was der Validator gesagt hat. null nur, wenn das Rendering fehlschlug, bevor der Validator lief. |
validation.valid | true, wenn kein Befund ein Fehler ist. Ein Rendering gelingt nur mit einer gültigen Datei. |
validation.schematron | Das Regelwerk, an dem das XML gemessen wurde, wie der Validator es nennt. |
validation.pdfa | PDF/A-3b, sobald die Datei daran geprüft wurde; null, wenn die Validierung beim XML geendet hat. |
validation.messages[] | Die Befunde, höchstens 50, Fehler zuerst. Jeder hat part (xml oder pdf), severity (error oder warning), rule (eine Regel der EN 16931 wie BR-CO-25, eine Regel der Syntax wie CII-SR-465, ein Code des XML-Schemas wie cvc-… oder eine Klausel von PDF/A wie 6.2.11.4.1-1; null, wo der Validator keine nennt), message und location (ein XPath in das XML, Zeile und Spalte bei einem Befund des XML-Schemas oder das Objekt des PDFs). |
validation.truncated | Vorhanden und true, wenn es mehr Befunde gab, als messages enthält. |
validation.id | Die eigene Kennung des Validators für diesen Bericht. Nennen Sie sie, wenn Sie zu einem Rendering fragen. |
validation.versions | Die Versionen der Teile des Validators, die die Datei geprüft haben: Mustang, veraPDF und PDFBox. Validator und Regeln nennt die Versionen im Einsatz. |
validation.xml_sha256, validation.pdf_sha256 | SHA-256 des geprüften XML und des geprüften PDF. Bei einem erfolgreichen Rendering ist das PDF die ausgelieferte Datei, derselbe Wert wie output_sha256 des Renderings, und das XML ist das XML darin. So nennt der Bericht genau die Datei, um die es geht, und lässt sich neben der Rechnung als Nachweis der Prüfung aufbewahren. pdf_sha256 ist null, wo der Validator das XML ablehnte, bevor es eine umgewandelte Datei gab. |
display | Die Sichtprüfung: checked nennt die Geschäftsbegriffe, nach denen im Text des PDFs gesucht wurde, missing die, die er nicht zeigt. null, wenn das Rendering fehlschlug, bevor die Prüfung lief. |
GET /v1/renders/{id}, die Liste der Renderings und die Webhook-Ereignisserender.completedundrender.failedtragen dasselbe Objekt.- Berichte von Renderings vor dem 9. Oktober 2026 haben weder
idnochversionsnoch Prüfsummen. - Mit
?output=binaryist die Antwort das PDF selbst. Lesen Sie den Bericht ausGET /v1/renders/{id}, mit der ID aus dem HeaderX-Render-Id. - Ein fehlgeschlagenes Rendering behält seinen Bericht. Lehnt der Validator die Datei ab, ist das Rendering
failed, seineinvoice.validationsagt, warum, unddisplayistnull, weil die Sichtprüfung nach dem Validator läuft. Das Problem der Anfrage trägt dasselbeeinvoice. - In der App hat das Blatt des Renderings im Render-Protokoll einen Abschnitt E-Rechnung mit dem Urteil, den Befunden und dem, was das PDF zeigt. Bericht (JSON) lädt den Bericht als Datei herunter. Ein Echtes Rendering im Editor zeigt denselben Abschnitt unter seinem Ergebnis: Es ist eine echte E-Rechnung, validiert wie ein API-Rendering, kostenlos wie jede Vorschau und ein Test-Rendering mit der Testmarkierung (siehe Pläne und Einheiten).
Das XML speichern
Mit "xml": "both" (im Editor Das XML als eigene Datei speichern) wird das XML als zweite Datei neben dem PDF gespeichert. Es ist dasselbe XML, das im PDF eingebettet ist.
- Die Datei trägt den Namen des PDFs mit
.xml: Zuinvoice-RE-2026-0001.pdfgehörtinvoice-RE-2026-0001.xml. einvoice.xml_urlist der Link darauf.download_urlbleibt der Link zum PDF.- Sie wird mit dem PDF aufbewahrt und gelöscht: dieselbe Aufbewahrung, dasselbe
expires_in, dasselbe vorzeitige Löschen. - Mit eigenem Speicher wird das XML wie das PDF in Ihren Bucket kopiert. Nennt das Rendering mit
storage.keysein eigenes Objekt, erhält das XML diesen Schlüssel mit der Endung.xml.
Mit embedded, dem Standard, ist xml_url null, und das XML gibt es nur im PDF. Vorschauen im Editor speichern nichts.
Womit sie sich nicht kombinieren lässt
- Ein Passwort.
post.passwordzusammen mit einer E-Rechnung beantwortet die API mit400 invalid_requestundfield: "post.password", egal wo die E-Rechnung festgelegt wurde: PDF/A verbietet Verschlüsselung. Für ein verschlüsseltes gewöhnliches PDF aus einer Rechnungsvorlage senden Siepost.einvoice: false. - Jede Ausgabe außer PDF. Eine Anfrage, die eine E-Rechnung für eine Bild-,
docx- oderpptx-Ausgabe verlangt, beantwortet die API mit400 invalid_requestundfield: "post.einvoice". Die eigene Festlegung einer Vorlage wird dort einfach nicht angewendet: Ein Bild aus einer Rechnungsvorlage bleibt ein Bild. - Deduplizierung. Eine E-Rechnung wird nie aus dem Cache der Deduplizierung beantwortet; jede Anfrage rendert.
- Die PDF-Tools danach. Die PDF-Tools
merge,watermarkundprotectnehmen das PDF einer E-Rechnung wie jedes andere PDF, und was sie zurückgeben, ist eine neue Datei, die nie validiert wurde: Eine zusammengeführte Datei trägt kein XML, eine geschützte ist verschlüsselt, und eine mit Wasserzeichen wurde neu gespeichert. Die E-Rechnung ist die Ausgabe des Renderings selbst; versenden und archivieren Sie diese.
post.merge_after und ein Wasserzeichen funktionieren, als Text oder als Bild. Ein Text-Wasserzeichen deckt lateinische, griechische und kyrillische Buchstaben ab, weil seine Schrift in die Datei eingebettet wird; nehmen Sie für andere Schriften ein Bild. Die E-Rechnung ist der letzte Schritt, nach dem Zusammenführen, dem Wasserzeichen und der Testmarkierung; umgewandelt und validiert wird also das Dokument, wie es ausgeliefert wird. Auch PDFs, die Sie anhängen, müssen PDF/A genügen: Eines, dessen Schriften nicht eingebettet sind, lässt das Rendering mit pdfa_conversion_failed fehlschlagen.
Als PDF/UA-1-Datei
Eine E-Rechnung kann zugleich ein PDF/UA-1-Dokument sein, das Format, nach dem ein Screenreader ein PDF liest. Setzen Sie pdf.ua neben die E-Rechnung, in den Einstellungen der Vorlage oder in der Anfrage, für eine HTML-Vorlage oder für HTML, das Sie senden. In der settings.json einer Vorlage:
{
"pdf": {
"ua": true,
"metadata": { "title": "Rechnung {{ _invoice.number }}" }
},
"einvoice": { "profile": "en16931", "flavour": "zugferd" }
}
Das PDF/A-3b behält die Tags der Seite, und die fertige E-Rechnung wird in derselben Anfrage gegen PDF/UA-1 ebenso geprüft wie gegen PDF/A-3b. Das Rendering trägt dann accessibility neben einvoice. Eine Datei, die PDF/UA-1 nicht besteht, lässt das Rendering mit 422 pdfua_validation_failed fehlschlagen, dessen Problem beide Berichte trägt, oder wird mit { "check": "report" } als die gültige E-Rechnung ausgeliefert, die sie ist, ohne die PDF/UA-Kennung. Die Prüfung kostet keine eigene Einheit.
Zwei Dinge gehen damit nicht zusammen: post.merge_after, weil ein Zusammenführen die Tags des Angehängten verwirft, und Word- und PowerPoint-Vorlagen, deren PDF aus einem anderen Konverter kommt. Eine E-Rechnung aus einer Word-Vorlage ist eine E-Rechnung ohne pdf.ua.
Pläne und Einheiten
Live-E-Rechnungen setzen den Starter-Plan oder höher voraus. Test-Renderings erzeugen E-Rechnungen in jedem Plan, bis zu einer Anzahl pro Tag, jede eine gültige Datei mit der Testmarkierung auf ihren Seiten:
| Plan | Live-Renderings | Test-Renderings pro Tag |
|---|---|---|
| Free | abgelehnt mit 403 forbidden | 20 |
| Starter | ja | 100 |
| Pro | ja | 500 |
| Business | ja | 2.000 |
| Enterprise | ja | nicht gezählt |
In den bezahlten Plänen ist die Zahl pro Tag ein Dreißigstel der monatlichen Einheiten des Plans.
- Test-Renderings sind Renderings mit einem Test-Schlüssel, Testanfrage senden im API-Tab des Editors und das Echte Rendering im Editor. Jede Test-E-Rechnung trägt die Testmarkierung, in jedem Plan. Live-Renderings werden weder gezählt noch markiert.
- Gezählt wird für die Organisation, und die Zählung beginnt um Mitternacht UTC neu. Gezählt wird nach allen anderen Prüfungen; eine Anfrage, die aus einem anderen Grund abgelehnt wird, verbraucht also nichts, ein Rendering, das danach fehlschlägt, hat seine Zählung verbraucht. Ein Batch zählt einen Eintrag für jedes Element, das eine E-Rechnung erzeugt, alle oder keinen. Ein Test-Rendering, sobald der Tag aufgebraucht ist, beantwortet die API mit
403 forbiddenund"feature": "einvoice",limit,usedundresets_at. - In einem Plan ohne E-Rechnungen beantwortet die API ein Live-Rendering, das eine erzeugt, mit
403 forbiddenund"feature": "einvoice", auch bei einer Vorlage, die eine E-Rechnung festlegt. Ein Rendering kommt nie stattdessen als gewöhnliches PDF zurück. Eine Vorlage, die eine E-Rechnung festlegt, etwa eine Kopie des Beispiels, lässt sich in der Seiteneinrichtung ausschalten, oder pro Anfrage mitpost.einvoice: false, und rendert dann gewöhnliche PDFs. - Einheiten. Eine E-Rechnung kostet 1 Einheit zusätzlich zu den Einheiten des Renderings. Eine zweiseitige Rechnung kostet 2 Einheiten: 1 für das PDF, 1 für die E-Rechnung. Test-Renderings, Vorschauen und echte Renderings im Editor kosten nichts, ein fehlgeschlagenes Rendering ebenso wenig. Siehe Einheiten.
Fehler
Bevor etwas gerendert oder gezählt wird, prüft die API eine Anfrage in dieser Reihenfolge: Profil und flavour, die Ausgabe, den Plan, ein Passwort, den _invoice-Block. Die Zählung der Test-E-Rechnungen des Tages kommt zuletzt.
| Status | Code | Wann |
|---|---|---|
| 422 | einvoice_data_invalid | Der _invoice-Block fehlt oder verletzt eine Regel, beantwortet, bevor etwas gerendert wird, mit jedem Befund in errors (siehe Wie der Block geprüft wird); oder der Validator hat die fertige Datei abgelehnt, und das Problem trägt den Bericht in einvoice. |
| 422 | einvoice_profile_unsupported | Ein Profil oder flavour, das Formfeed nicht erzeugt: minimum, basic-wl, extended, xrechnung oder ein unbekannter Wert. |
| 422 | einvoice_display_mismatch | Mit display_check: "strict" zeigt das PDF einen Wert des XML nicht. |
| 500 | pdfa_conversion_failed | Das PDF ließ sich nicht in ein gültiges PDF/A-3b umwandeln. Die Befunde stehen in einvoice, oder die Meldung des Konverters in reason. Die bekannten Ursachen: eine Schrift, die nicht eingebettet ist, die ein angehängtes PDF mitbringen kann, und eine Schrift, deren eingebettetes Programm nicht zu den Breiten passt, die das PDF nennt; so schreibt der Office-Konverter chinesischen, japanischen und koreanischen Text einer Word- oder PowerPoint-Vorlage. Eine Wiederholung hilft nicht, und asynchrone Renderings werden dafür nicht wiederholt. |
| 503 | conformance_unavailable | Der Dienst, der umwandelt und validiert, ist ausgelastet oder nicht erreichbar, oder E-Rechnungen sind in der Region nicht verfügbar. Wiederholen Sie nach Retry-After; asynchrone Renderings und Batches wiederholen es von selbst. |
| 400 | invalid_request | Eine E-Rechnung zusammen mit einem Passwort, für eine andere Ausgabe als PDF, oder mit einer Option oder einem Wert, den es nicht gibt. |
| 403 | forbidden | Mit "feature": "einvoice". Der Plan enthält keine E-Rechnungen und das Rendering ist ein Live-Rendering, oder die Test-E-Rechnungen des Tages sind aufgebraucht. |
Die Umwandlung kann auch mit zwei allgemeinen Codes enden: 422 output_too_large, wenn das PDF zu groß ist, um eine E-Rechnung zu werden, und 504 render_timeout, wenn die Umwandlung nicht rechtzeitig fertig wird. Eine E-Rechnung, die zugleich eine PDF/UA-1-Datei ist, kann außerdem mit pdfua_validation_failed fehlschlagen.
Kommandozeile
Die CLI prüft und schreibt E-Rechnungen offline, mit dem Code, den auch die API ausführt.
formfeed validate misst den _invoice-Block jedes Datensatzes an den Regeln, für jede Vorlage, deren settings.json eine E-Rechnung festlegt. Ein Befund nennt die Datei, die Zeile und die Spalte, das Feld und die Regel, und der Befehl endet mit 1:
error invoice/data/storno.json:63:7 _invoice.totals.due is 1000.00, but grand less prepaid plus rounding is 1142.40 (BR-CO-16) [einvoice-sum]
1 error(s)
Ein Datensatz ohne den Block und ein Profil, das nicht angeboten wird, sind ebenfalls Fehler. Bei einer Bildvorlage ist die Festlegung eine Warnung, weil eine solche Vorlage nie eine E-Rechnung erzeugt.
formfeed einvoice <slug> schreibt das XML, das ein Rendering einbetten würde, ohne API-Schlüssel:
formfeed einvoice invoice --out out/rechnung.xml
invoice: ZUGFeRD 2.5, EN 16931, RE-2026-0001, 1 line(s), 1142.40 EUR -> out/rechnung.xml
checked offline against the rules the API applies before a render; the official validator runs with the render
--data nimmt den Namen eines Datensatzes oder eine JSON-Datei, --out die Datei, die geschrieben wird (ohne die Option <slug>.xml), und --profile hat Vorrang vor den Einstellungen der Vorlage. Eine Vorlage, die nichts festlegt, erhält die Standardwerte. Ein Block, der eine Regel verletzt, schreibt keine Datei und listet, was nicht stimmt. Das XML wird an den Regeln geprüft, die die API vor einem Rendering anwendet; der Validator selbst läuft mit dem Rendering, nicht hier.
formfeed render <slug> sendet die lokale Vorlage mit dem, was settings.json festlegt, und dem _invoice-Block des Datensatzes, und gibt den Bericht des Renderings aus:
e-invoice ZUGFeRD, EN 16931, specification 2.5
valid rules of the XML (Factur-X 1.09.2 (Mustang 2.26.0)), PDF/A-3b
shown invoice number, net total, total
warn not in the text of the PDF: VAT total
rnd_7da09ab6cd9946b798c187c2a9350852: 1 page(s), 0 unit(s) -> invoice.pdf
Eine lokale Word- oder PowerPoint-Datei rendert als gewöhnliches PDF, mit einer Warnung: Ihre E-Rechnung entsteht, wenn die veröffentlichte Vorlage rendert, was formfeed render --remote tut.
Was Formfeed nicht tut
Formfeed erzeugt und validiert die Datei. Es übernimmt nicht:
- Den Versand. Formfeed ist weder ein Peppol-Access-Point noch eine französische plateforme agréée. Sie, Ihre Plattform oder Ihr Access Point stellen die Rechnung zu.
- Die Archivierung für die gesetzliche Frist. Siehe unten.
- Die Buchung. Die Buchhaltung bleibt in Ihrem Buchhaltungssystem.
- Die Umsatzsteuer. Welche Kategorie, welcher Satz und welcher Befreiungsgrund gelten, entscheiden Sie oder Ihre Steuerberatung. Formfeed leistet keine Steuerberatung.
- Die Signatur. Die Datei trägt keine elektronische Signatur.
- XRechnung oder UBL. Beides ist nicht verfügbar. Auch die Profile
minimum,basic-wlundextendedwerden nicht angeboten.
Sie stellen die Rechnung aus und stehen für ihren Inhalt und ihre umsatzsteuerliche Behandlung ein. Formfeed steht für das Format ein.
Archivierung und der Bericht
Formfeed ist kein Archiv. Ausgaben werden nur für die Aufbewahrungsfrist Ihres Plans und Workspace aufbewahrt (1 Tag bei Free, 30 Tage bei Starter, 365 Tage bei Pro; Business setzt keine Grenze, aber eine Einstellung des Workspace oder eine Löschung beendet sie trotzdem). Nichts in Formfeed bewahrt eine Rechnung die Jahre auf, die das Gesetz von Ihnen verlangt, und nichts schützt sie davor, gelöscht zu werden. Archivieren Sie jede Rechnung selbst:
- Archivieren Sie die ausgelieferte Datei selbst oder die XML-Ausgabe von
xml: both. Auf das XML in der Datei kommt es an. Archivieren Sie nie einen Ausdruck, der kein XML hat, und kein PDF, das ein anderes Programm erneut gespeichert hat: Es ist nicht mehr die Datei, die validiert wurde. - Bewahren Sie den Validierungsbericht daneben auf. Er ist das Objekt
einvoicedes Renderings, aus der Antwort, ausGET /v1/renders/{id}oder aus Bericht (JSON) im Render-Protokoll. Er hält fest, an welchen Regeln die Datei gemessen wurde und dass sie bestanden hat, am Tag, an dem die Rechnung ausgestellt wurde. - Wissen Sie, was der Bericht belegt. Die Validierung belegt das Format und die Geschäftsregeln, nicht den Inhalt. Eine Rechnung mit einem falschen Umsatzsteuersatz besteht sie.
Eigener Speicher kopiert jedes PDF und das XML von xml: both beim Rendern in Ihren eigenen Bucket.