REST-API-Referenz

E-Rechnungen im Format ZUGFeRD/Factur-X (EN 16931) per HTTP prüfen und erzeugen, dazu die Zahlungsverkehr-Endpoints für SEPA- und Auslandszahlungsdateien, BIC-Lookup und Adress-Strukturierung. Server-zu-Server gedacht, nicht für Aufrufe aus dem Browser.

Authentifizierung

Jeder Aufruf unter /api/v1 braucht ein Credential. Einen API-Key legen Sie in Ihrem Konto im Bereich „API und Anwendungen“ an. Der Key wird als Bearer-Token gesendet:

Authorization: Bearer fnsm_…

Das Prüfen (/verify) ist kostenlos, verlangt aber ebenfalls ein Credential. Das Erzeugen (/invoices) läuft mit aktivem Abo, mit Guthaben (Credits: 1 je Rechnung, ohne Wasserzeichen und ohne Tageslimit) oder aus dem kostenlosen Tageskontingent mit Wasserzeichen. Das Auslesen (/extractions) läuft mit Abo oder kostet 1 Credit je Aufruf; steckt in der PDF schon eine E-Rechnung, kommen die Felder direkt aus der eingebetteten XML. Statt eines statischen Keys akzeptiert derselbe Header auch ein OAuth-2.1-Zugriffstoken (Bearer fnso_…), sinnvoll, wenn eine Integration ein fremdes finisma-Konto verbindet statt einen eigenen Key zu verwalten.

Ob ein Credential trägt und was es darf, beantwortet GET https://finisma.de/api/v1/me: Konto, Abo-Status, bei OAuth die zugestimmten Scopes und die gebuchten Bausteine (features: converter, payments, paymentsAbroad). Damit lässt sich vorab prüfen, ob ein Konto etwa /payments/bank-lookup nutzen darf, statt es erst am 403 einer echten Anfrage zu merken. Für Verbindungstests in Integrationen gedacht.

Selbstauskunft: Formate und Codelisten

Fünf anonyme GET-Endpunkte beschreiben, was die API kann — erzeugt aus denselben Quelldateien wie Validierung und Generator, deshalb immer aktuell. Gedacht für Integratoren, die Belegarten, Einheiten oder Steuerkategorien VOR dem ersten Aufruf mappen wollen, und für Werkzeuge, die ihre Auswahlfelder live füllen.

curl https://finisma.de/api/v1/formats
curl https://finisma.de/api/v1/codelists/document-types
curl https://finisma.de/api/v1/codelists/units
curl https://finisma.de/api/v1/codelists/vat-categories
curl https://finisma.de/api/v1/codelists/payment-means
curl https://finisma.de/api/v1/codelists/attachment-types

/formats nennt je Format die Fähigkeiten (erzeugen, prüfen), Anhangsregeln, Limits und Prüf-Engines. /codelists/units liefert zusätzlich die Normalisierungs-Aliasse ("Stck" wird C62): was dort steht, dürfen Sie schicken, der Rest fällt auf Stück zurück.

Rechnung prüfen

POST https://finisma.de/api/v1/verify mit der PDF als multipart-Feld pdf. Antwort ist ein JSON-Prüfbericht (Struktur, EN-16931-Regeln, PDF/A-3-Anbindung).

curl -X POST https://finisma.de/api/v1/verify \
  -H "Authorization: Bearer $FINISMA_KEY" \
  -F "pdf=@rechnung.pdf;type=application/pdf"

Geschäftspartner prüfen: USt-IdNr. und Peppol

Zwei kostenlose GET-Endpunkte für den Preflight vor dem Rechnungsversand (auch als MCP-Tools check_vat_id und check_peppol_participant verfügbar): /vat/check prüft eine USt-IdNr. live gegen VIES, das EU-Bestätigungsverfahren. /peppol/check schlägt per SML/SMP nach, ob ein Partner im Peppol-Netz erreichbar ist, und listet seine Dokumenttypen — als Eingabe genügt eine GLN, eine deutsche USt-IdNr. oder eine Leitweg-ID.

curl "https://finisma.de/api/v1/vat/check?vatId=DE325821677" \
  -H "Authorization: Bearer $FINISMA_KEY"

curl "https://finisma.de/api/v1/peppol/check?participant=0088:4399901550926" \
  -H "Authorization: Bearer $FINISMA_KEY"

Zwei Zustände sind bewusst KEINE Fehler: status: "unavailable" beim VIES-Check heißt, der Mitgliedsstaat war nicht erreichbar — nicht, dass die Nummer ungültig ist. Und registered: false beim Peppol-Check ist ein normales Ergebnis; die Rechnung lässt sich dann klassisch als ZUGFeRD-PDF per E-Mail zustellen.

Rechnungsdaten aus einer PDF auslesen

POST https://finisma.de/api/v1/extractions mit der PDF als multipart-Feld pdf. Antwort ist JSON mit invoice (die EN-16931-Felder), issues (offene Probleme) und repairedPaths. Gedacht als Vorstufe zu /invoices: auslesen, prüfen lassen, korrigieren, erzeugen.

curl -X POST https://finisma.de/api/v1/extractions \
  -H "Authorization: Bearer $FINISMA_KEY" \
  -F "pdf=@rechnung.pdf;type=application/pdf"

Gelesen wird mit derselben Pipeline wie im Web-Formular: Textpfad oder Vision-Modell je nach PDF, eine Reparaturschleife für unvollständige Modellantworten und deterministische Nachträge für IBAN und USt-IdNr. aus dem Rechnungstext.

Dieser Endpunkt erfordert ein aktives Abo oder Guthaben (1 Credit je Aufruf); das kostenlose Tageskontingent der Erzeugung gilt hier nicht. Je Aufruf läuft ein kostenpflichtiges Sprachmodell; das Kontingent von /invoices deckt das nicht. Ohne Abo kommt 403 subscription_required. Das Rate-Limit liegt bei 10 Aufrufen pro Minute und Credential, deutlich unter dem der übrigen Endpoints, weil jeder Aufruf ein Modell beschäftigt.

Platzhalter: was repairedPaths bedeutet

Ein Sprachmodell liest nicht jedes Feld sicher. Wo die Extraktion nichts Verwertbares fand, steht ein Platzhalter statt eines geratenen Werts, und der Pfad des Feldes steht in repairedPaths (Punkt-Notation, etwa seller.name oder lines.0.lineNetAmount).

PlatzhalterWoVerhalten bei /invoices
KORRIGIERENTextfelderblockiert als EXTRACTION_UNCERTAIN
1900-01-01Datumsfelderblockiert als EXTRACTION_UNCERTAIN
0Betrags- und Mengenfelderläuft durch, wenn die Summen zufällig aufgehen. Nur repairedPaths verrät diesen Fall.

Wer repairedPaths ignoriert, schickt die Platzhalter unbemerkt weiter in /invoices und erzeugt eine formal gültige Rechnung mit falschem Inhalt. Die Liste gehört deshalb einem Menschen vorgelegt oder mindestens geloggt.

ZUGFeRD-Rechnung erzeugen

POST https://finisma.de/api/v1/invoices nimmt die visuelle PDF (pdf) und die strukturierten EN-16931-Daten (invoice, JSON) und bettet das CII-XML ein. Die Antwort ist standardmäßig die fertige PDF/A-3 binär. Mit Accept: application/json kommt stattdessen { pdf_base64, bytes }. Das muss ausdrücklich angefordert werden: die Vorgabe vieler HTTP-Bibliotheken lautet application/json, text/plain, */*, und darauf antwortet die API binär, damit niemand ungefragt eine Base64-Hülle in eine .pdf-Datei schreibt.

curl -X POST https://finisma.de/api/v1/invoices \
  -H "Authorization: Bearer $FINISMA_KEY" \
  -F "pdf=@rechnung.pdf;type=application/pdf" \
  -F "invoice=@invoice.json;type=application/json" \
  -F "filename=rechnung-zugferd.pdf" \
  -o rechnung-zugferd.pdf

Optionale Anhänge werden als weitere attachments-Felder angehängt (PDF, PNG, JPEG, CSV, XLSX, ODS, XML; je max. 10 MB). Sie landen als rechnungsbegründende Unterlage im XML (BG-24, Dokumentenart 916) und zugleich als eingebettete Datei in der PDF/A-3.

Wozu eine Datei gehört, sagt attachmentDescriptions(BT-123): je Anhang ein Text-Part, in derselben Reihenfolge wie die Datei-Parts. Ohne Angabe steht dort der Dateiname, und der Empfänger muss raten.

curl -X POST https://finisma.de/api/v1/invoices \
  -H "Authorization: Bearer $FINISMA_KEY" \
  -F "pdf=@rechnung.pdf;type=application/pdf" \
  -F "invoice=@invoice.json;type=application/json" \
  -F "attachments=@stunden.csv;type=text/csv" \
  -F "attachmentDescriptions=Stundennachweis März 2026" \
  -o rechnung-zugferd.pdf

Nur das XML (format=xml)

Wer das Sichtdokument selbst erzeugt oder nur an eine Behörde einreicht, braucht die PDF nicht. ?format=xml liefert das reine CII-XML. Dieser Weg ruft den Einbettungsdienst nicht auf und ist deshalb deutlich schneller; der pdf-Part entfällt, es genügt invoice.

curl -X POST "https://finisma.de/api/v1/invoices?format=xml" \
  -H "Authorization: Bearer $FINISMA_KEY" \
  -F "invoice=@invoice.json;type=application/json" \
  -F "filename=rechnung.xml" \
  -o rechnung.xml

Statt des Query-Parameters wirkt auch Accept: application/xml, nach derselben engen Regel wie bei JSON: Der Header muss application/xml nennen und darf weder application/pdf noch */* enthalten. Mit Accept: application/json kommt statt des XML-Dokuments { xml, bytes }.

Erzeugt wird byteidentisch dasselbe XML, das der PDF-Weg einbettet: gleiche Eingabe, gleiche Prüfung (Schema und EN-16931-Geschäftsregeln), gleiche Fehlercodes. Anhänge bleiben möglich und landen als rechnungsbegründende Unterlage im XML (BT-125, Base64 im Dokument selbst); die PDF/A-3-Ebene entfällt naturgemäß. Für öffentliche Auftraggeber erzeugt dieser Weg mit "profile": "xrechnung" die XRechnung 3.0 — Beispiel 5 zeigt die Pflichtfelder.

Der XML-Weg setzt ein aktives Abo oder Guthaben voraus. Das kostenlose Tageskontingent arbeitet mit dem finisma-Prüfsiegel als Wasserzeichen, und ein XML kann kein Wasserzeichen tragen. Mit Guthaben kostet der Aufruf einen Credit; ohne Abo und ohne Guthaben antwortet der Endpunkt mit 403 subscription_required; der PDF-Weg bleibt kostenlos nutzbar.

Beispiel 1: die kleinstmögliche Rechnung

So wenig braucht eine gültige E-Rechnung: zwei Parteien, eine Position, eine Steuerzeile, drei Summen. Alles Übrige sind Defaults des Schemas (Währung EUR, Einheit Stück, Steuerkategorie S). Als invoice.json gespeichert läuft das Beispiel unverändert durch den curl-Aufruf oben.

{
  "invoiceNumber": "2026-001",
  "issueDate": "2026-08-19",
  "dueDate": "2026-09-02",
  "seller": {
    "name": "Muster GmbH",
    "vatId": "DE123456789",
    "address": { "line1": "Hauptstr. 1", "postalCode": "20095", "city": "Hamburg" }
  },
  "buyer": {
    "name": "Kunde AG",
    "address": { "line1": "Marktplatz 5", "postalCode": "10115", "city": "Berlin" }
  },
  "lines": [
    { "name": "Beratung August", "quantity": 1, "unitPriceNet": 100, "vatRatePercent": 19, "lineNetAmount": 100 }
  ],
  "vatBreakdown": [
    { "ratePercent": 19, "basisAmount": 100, "taxAmount": 19 }
  ],
  "totalNet": 100,
  "totalVat": 19,
  "totalGross": 119
}

Beispiel 2: Gutschrift mit Bezug auf die Ursprungsrechnung

Gutschriften, Korrektur-, Abschlags- und Schlussrechnungen laufen über dasselbe Feld: documentTypeCode nach UNCL 1001 (381 Gutschrift, 384 Korrekturrechnung, 386 Vorausrechnung, 326 Teilrechnung, 389 Selbstfakturierung, 383 Belastungsanzeige, 875 bis 877 Bauleistung; ohne Angabe gilt 380, Handelsrechnung). Bei Gutschrift und Korrektur gehört der Bezug auf die Ursprungsrechnung dazu (precedingInvoiceNumber und precedingInvoiceDate, BT-25/BT-26). Die Beträge bleiben in jedem Fall positiv: die Richtung trägt der Dokumenttyp, nicht das Vorzeichen. Abschlags- und Schlussrechnungen übergeben zusätzlich paidAmount (BT-113, bereits gezahlter Bruttobetrag).

{
  "invoiceNumber": "GS-2026-017",
  "documentTypeCode": "381",
  "precedingInvoiceNumber": "2026-001",
  "precedingInvoiceDate": "2026-08-19",
  "issueDate": "2026-08-26",
  "buyerReference": "04011000-1234512345-06",
  "purchaseOrderReference": "878561",
  "seller": {
    "name": "Muster GmbH",
    "vatId": "DE123456789",
    "address": { "line1": "Hauptstr. 1", "postalCode": "20095", "city": "Hamburg" }
  },
  "buyer": {
    "name": "Kunde AG",
    "address": { "line1": "Marktplatz 5", "postalCode": "10115", "city": "Berlin" }
  },
  "lines": [
    {
      "name": "Rückvergütung Beratung August",
      "sellerArticleId": "BER-01",
      "globalArticleId": "4012345678901",
      "quantity": 1,
      "unitPriceNet": 40,
      "vatRatePercent": 19,
      "lineNetAmount": 40
    }
  ],
  "vatBreakdown": [
    { "ratePercent": 19, "basisAmount": 40, "taxAmount": 7.6 }
  ],
  "totalNet": 40,
  "totalVat": 7.6,
  "totalGross": 47.6,
  "payment": {
    "reference": "Gutschrift zu 2026-001",
    "terms": "Erstattung binnen 14 Tagen auf das bekannte Konto."
  },
  "notes": [
    "Gutschrift wegen reduziertem Stundenumfang."
  ]
}

Beispiel 3: Bau-Abschlagsrechnung mit Projekt, Vertrag und Nachlass

Bau- und Projektrechnungen tragen Kopf-Felder, die im Handel selten sind, für die Zuordnung beim Empfänger aber entscheidend: projectReference (BT-11, die Buchhaltung des Auftraggebers bucht auf das Projekt), contractReference (BT-12, niemals in die Bestellnummer schreiben), despatchAdviceRef (BT-16, Lieferschein), invoicingPeriod (BG-14, der Leistungszeitraum mit start und end als ISO-Datum, eines von beiden genügt) und delivery (BG-13, die Baustelle).

allowanceCharges (BG-20/BG-21) bildet Abzüge und Zuschläge auf die ganze Rechnung ab, etwa den Nachlass: charge: false ist ein Abzug, true ein Zuschlag, der Betrag steht immer ohne Vorzeichen. Die Rechenkette folgt EN 16931: totalNet ist der Betrag nach den Abzügen (Positionssumme − Abzüge + Zuschläge, BR-CO-13), und die Bemessungsgrundlage des betroffenen Steuersatzes sinkt entsprechend. Ein Sicherheitseinbehalt ist bewusst kein Abzug: Er mindert weder Betrag noch Steuer, sondern gehört im Klartext in die Zahlungsbedingungen — geschuldet wird der volle Betrag, zurückgehalten nur ein Teil davon. Rabatte auf eine einzelne Position führen Sie als lines[].allowanceCharges (BG-27/BG-28, siehe den Mengenrabatt der ersten Position): lineNetAmount ist der Betrag nach diesen Einträgen, ein Grund (Text oder Code) ist Pflicht (BR-42), und die Validierung rechnet die Zeilenkette Menge × Preis − Abzüge + Zuschläge vor der Erzeugung nach.

{
  "invoiceNumber": "2026-017",
  "documentTypeCode": "875",
  "issueDate": "2026-08-28",
  "dueDate": "2026-09-11",
  "currency": "EUR",
  "contractReference": "V-2026-0815",
  "projectReference": "P-2026-042",
  "despatchAdviceRef": "LS-2026-1183",
  "invoicingPeriod": { "start": "2026-08-03", "end": "2026-08-28" },
  "seller": {
    "name": "Trockenbau Muster GmbH",
    "vatId": "DE123456789",
    "address": { "line1": "Werkstr. 1", "city": "Hamburg", "postalCode": "20095", "countryCode": "DE" }
  },
  "buyer": {
    "name": "Generalunternehmer AG",
    "address": { "line1": "Bauhof 3", "city": "Berlin", "postalCode": "10115", "countryCode": "DE" }
  },
  "delivery": {
    "name": "BV Musterpark, Haus B",
    "address": { "line1": "Parkallee 12", "city": "Potsdam", "postalCode": "14469", "countryCode": "DE" }
  },
  "lines": [
    {
      "name": "Metallständerwände gem. Aufmaß",
      "quantity": 250,
      "unitCode": "MTK",
      "unitPriceNet": 50,
      "vatRatePercent": 19,
      "allowanceCharges": [
        { "charge": false, "amount": 500, "reason": "Mengenrabatt" }
      ],
      "lineNetAmount": 12000
    },
    { "name": "Brandschutzverkleidung F90", "quantity": 70, "unitCode": "MTK", "unitPriceNet": 50, "vatRatePercent": 19, "lineNetAmount": 3500 }
  ],
  "allowanceCharges": [
    { "charge": false, "percent": 2, "basisAmount": 15500, "amount": 310, "reason": "Nachlass", "categoryCode": "S", "ratePercent": 19 }
  ],
  "vatBreakdown": [
    { "ratePercent": 19, "basisAmount": 15190, "taxAmount": 2886.10, "categoryCode": "S" }
  ],
  "totalNet": 15190,
  "totalVat": 2886.10,
  "totalGross": 18076.10,
  "payment": {
    "iban": "DE02120300000000202051",
    "bic": "BYLADEM1001",
    "reference": "2026-017",
    "terms": "Zahlbar innerhalb 14 Tagen. Sicherheitseinbehalt 5 % = 903,81 EUR, fällig nach Abnahme."
  }
}

Beispiel 4: Reverse Charge (Steuerschuldnerschaft des Leistungsempfängers)

Für Leistungen an Unternehmen im EU-Ausland (§ 13b UStG) schuldet der Empfänger die Umsatzsteuer. In der Rechnung heißt das: alle Sätze 0 %, Kategorie AE in der vatBreakdown-Aufschlüsselung — die Positionen erben die Kategorie über ihren Steuersatz, dort genügt vatRatePercent: 0. Führt eine Rechnung zwei Aufschlüsselungen mit demselben Satz (etwa E und AE, beide 0 %), setzen Sie zusätzlich categoryCode an den betroffenen Positionen (BT-151), sonst ist die Zuordnung mehrdeutig und die Validierung lehnt ab. Pflicht ist die USt-IdNr. des Käufers (buyer.vatId): Ohne sie lehnt die Validierung vor der Erzeugung mit BR-AE-02 ab, es wird weder eine Datei erzeugt noch ein Credit verbraucht. Der Befreiungsgrund (exemptionReason, BT-120) ist optional; ohne Angabe schreibt der Generator den Standardtext „Reverse charge“, ein eigener Text wie hier gewinnt. Zusätzlich möglich: exemptionReasonCode (BT-121, für Reverse Charge VATEX-EU-AE).

{
  "invoiceNumber": "2026-042",
  "issueDate": "2026-09-05",
  "dueDate": "2026-09-19",
  "seller": {
    "name": "Muster GmbH",
    "vatId": "DE123456789",
    "address": { "line1": "Hauptstr. 1", "postalCode": "20095", "city": "Hamburg" }
  },
  "buyer": {
    "name": "Beispiel GmbH",
    "vatId": "ATU12345675",
    "address": { "line1": "Ringstraße 12", "postalCode": "1010", "city": "Wien", "countryCode": "AT" }
  },
  "lines": [
    { "name": "Softwareentwicklung September", "quantity": 20, "unitCode": "HUR", "unitPriceNet": 90, "vatRatePercent": 0, "lineNetAmount": 1800 }
  ],
  "vatBreakdown": [
    {
      "ratePercent": 0,
      "basisAmount": 1800,
      "taxAmount": 0,
      "categoryCode": "AE",
      "exemptionReason": "Steuerschuldnerschaft des Leistungsempfängers"
    }
  ],
  "totalNet": 1800,
  "totalVat": 0,
  "totalGross": 1800,
  "payment": {
    "iban": "DE02120300000000202051",
    "reference": "2026-042",
    "terms": "Zahlbar innerhalb 14 Tagen ohne Abzug"
  }
}

Für Marktplatz- und Factoring-Rechnungen gibt es außerdem payee (BG-10, der abweichende Zahlungsempfänger, Name ist Pflicht) und payment.accountName (BT-85, der Kontoinhaber); receivingAdviceRef (BT-15) ergänzt die Wareneingangsmeldung. Alle Felder stehen vollständig in der OpenAPI-Spezifikation.

Beispiel 5: XRechnung für öffentliche Auftraggeber

Behörden und andere öffentliche Auftraggeber in Deutschland verlangen die XRechnung. "profile": "xrechnung" erzeugt sie: Das XML trägt dann die KoSIT-CIUS-Kennung und den Peppol-Geschäftsprozess (BT-23/BT-24), und die Validierung prüft die zusätzlichen deutschen Pflichten vor der Erzeugung. Pflicht sind die Leitweg-ID in buyerReference (BT-10, teilt Ihnen der Auftraggeber mit), ein Verkäufer-Ansprechpartner mit Name, Telefon und E-Mail (seller.contact, BR-DE-2), Zahlungsangaben (payment mit IBAN oder Zahlungsart, BR-DE-1) und die elektronische Adresse beider Parteien (electronicAddress, BT-34/BT-49) — als E-Mail-Adresse (Schema EM, Standard) oder Leitweg-ID (Schema 0204). Die XRechnung ist ein reines XML-Format: Rufen Sie den Endpoint mit ?format=xml auf; der PDF-Weg (ZUGFeRD) lehnt das Profil ab.

{
  "profile": "xrechnung",
  "invoiceNumber": "2026-071",
  "issueDate": "2026-09-05",
  "dueDate": "2026-10-05",
  "buyerReference": "04011000-1234512345-06",
  "seller": {
    "name": "Muster GmbH",
    "vatId": "DE123456789",
    "address": { "line1": "Hauptstr. 1", "postalCode": "20095", "city": "Hamburg" },
    "contact": {
      "name": "Maria Muster",
      "phone": "+49 40 1234560",
      "email": "rechnung@muster-gmbh.de"
    },
    "electronicAddress": { "id": "rechnung@muster-gmbh.de" }
  },
  "buyer": {
    "name": "Stadt Beispielhausen, Amt für Gebäudemanagement",
    "address": { "line1": "Rathausplatz 1", "postalCode": "21079", "city": "Beispielhausen" },
    "electronicAddress": { "id": "04011000-1234512345-06", "scheme": "0204" }
  },
  "lines": [
    { "name": "Wartung Lüftungsanlage, 3. Quartal", "quantity": 1, "unitPriceNet": 1450, "vatRatePercent": 19, "lineNetAmount": 1450 }
  ],
  "vatBreakdown": [
    { "ratePercent": 19, "basisAmount": 1450, "taxAmount": 275.5 }
  ],
  "totalNet": 1450,
  "totalVat": 275.5,
  "totalGross": 1725.5,
  "payment": {
    "iban": "DE02120300000000202051",
    "reference": "2026-071",
    "terms": "Zahlbar innerhalb 30 Tagen ohne Abzug"
  }
}

Rechnungsdaten (invoice)

Die Datenstruktur folgt EN 16931. Vollständig und maßgeblich steht sie in der OpenAPI-Spezifikation (Schema Invoice), die direkt aus dem Validierungsschema erzeugt wird und damit immer aktuell ist. Ein Beispiel mit Zahlungsdaten, Skonto und Hinweisen:

{
  "invoiceNumber": "2026-001",
  "issueDate": "2026-07-22",
  "currency": "EUR",
  "seller": {
    "name": "Muster GmbH",
    "vatId": "DE123456789",
    "address": { "line1": "Hauptstr. 1", "city": "Hamburg", "postalCode": "20095", "countryCode": "DE" }
  },
  "buyer": {
    "name": "Kunde AG",
    "address": { "line1": "Marktplatz 5", "city": "Berlin", "postalCode": "10115", "countryCode": "DE" }
  },
  "lines": [
    { "name": "Beratung", "quantity": 10, "unitCode": "HUR", "unitPriceNet": 100, "vatRatePercent": 19, "lineNetAmount": 1000 }
  ],
  "vatBreakdown": [
    { "ratePercent": 19, "basisAmount": 1000, "taxAmount": 190, "categoryCode": "S" }
  ],
  "totalNet": 1000,
  "totalVat": 190,
  "totalGross": 1190,
  "payment": {
    "iban": "DE02120300000000202051",
    "bic": "BYLADEM1001",
    "reference": "2026-001",
    "terms": "Zahlbar innerhalb 30 Tagen ohne Abzug",
    "discountPercent": 2.25,
    "discountDays": 14
  },
  "notes": [
    "Eigentumsvorbehalt bis zur vollständigen Bezahlung."
  ]
}

Zahlungsbedingungen und Skonto

Skonto wird strukturiert über discountPercent und discountDays übergeben (Tage ab Rechnungsdatum). EN 16931 hat dafür kein eigenes Feld, deshalb schreibt finisma die KoSIT-Konvention in die Zahlungsbedingungen (BT-20): zuerst Ihr Text aus terms, darunter je Stufe eine Marker-Zeile.

Zahlbar innerhalb 30 Tagen ohne Abzug
#SKONTO#TAGE=14#PROZENT=2.25#

Liegt die Skonto-Frist nach dem Fälligkeitsdatum oder ist der Satz unplausibel hoch, wird die Rechnung weiterhin erzeugt, der Fall aber als Warnung gemeldet: formal gültig, inhaltlich prüfenswert.

Zahlungsart und SEPA-Lastschrift

Ohne Angabe schreibt der Generator bei gesetzter payment.iban die Zahlungsart 58 (SEPA-Überweisung, BT-81). payment.meansCode wählt explizit; die kuratierte Codeliste liefert GET /codelists/payment-means (30 Überweisung, 58 SEPA-Überweisung, 59 SEPA-Lastschrift). Für die Lastschrift (59) sind Mandatsreferenz (BT-89), Gläubiger-ID (BT-90) und das belastete Käuferkonto (BT-91) Pflicht — fehlt eines, lehnt die Validierung vor der Erzeugung ab; ein Empfängerkonto gehört dort nicht in die Datei.

"payment": {
  "meansCode": "59",
  "mandateId": "MANDAT-2026-042",
  "creditorId": "DE98ZZZ09999999999",
  "debitedIban": "DE02120300000000202051",
  "reference": "2026-042"
}

Artikelnummern und GTIN

Jede Position kann drei Kennungen tragen. Sie sind optional, aber der einzige Weg, Artikelnummern maschinell auswertbar zu übergeben statt sie in den Positionstext zu schreiben.

FeldBTBedeutung
sellerArticleIdBT-155Ihre eigene Artikel- oder Katalognummer.
buyerArticleIdBT-156Die Nummer, unter der der Kunde denselben Artikel führt (Bestell- oder EDV-Nummer).
globalArticleIdBT-157Normierte Kennung, in der Praxis eine GTIN. Eine EAN-13 ist eine GTIN-13 und gehört hierher.
globalArticleSchemeBT-157-1Nummernkreis der normierten Kennung, Code aus dem ISO-6523-Verzeichnis. Ohne Angabe wird 0160 (GS1 GTIN) angenommen.
"lines": [
  {
    "name": "Beratung",
    "sellerArticleId": "JH-1",
    "buyerArticleId": "4440",
    "globalArticleId": "4012345678901",
    "quantity": 10, "unitCode": "HUR", "unitPriceNet": 100,
    "vatRatePercent": 19, "lineNetAmount": 1000
  }
]

Abschlags- und Schlussrechnungen

Bereits geleistete Abschlagszahlungen kommen als paidAmount (Bruttobetrag, BT-113). Der offene Zahlbetrag wird daraus berechnet: totalGross − paidAmount. Wichtig: totalGross bleibt der volle Bruttobetrag der Gesamtleistung, nicht der offene Rest. Ohne paidAmount nennt die Rechnung den vollen Betrag als offen, und der Empfänger zahlt die Anzahlung ein zweites Mal. Diese Konstellation ist am EN-16931-Validator unsichtbar (die Rechnung bleibt in sich schlüssig), deshalb prüft finisma sie zusätzlich anhand des Rechnungstextes und weist im Prüfbericht darauf hin. Ein Sicherheitseinbehalt ist keine Abschlagszahlung: Er gehört weder in paidAmount noch in allowanceCharges, sondern im Klartext in die Zahlungsbedingungen (siehe Beispiel 3).

Freitext-Hinweise

notes nimmt Hinweise der Rechnung auf (Eigentumsvorbehalt, Leistungszeitraum, Reverse-Charge-Vermerk). Jeder Eintrag wird eine eigene Notiz im XML (BT-22), höchstens 20 Einträge.

Zahlungsverkehr-Endpoints

Die Zahlungsverkehr-Endpoints liegen wie alles andere unter /api/v1: https://finisma.de/api/v1/payments/sepa und https://finisma.de/api/v1/payments/axz erzeugen die Zahlungsdateien, https://finisma.de/api/v1/payments/bank-lookup und https://finisma.de/api/v1/payments/structure-addresses liefern zu. Alle vier akzeptieren dasselbe Bearer-Credential wie oben (API-Key oder OAuth-Token, unabhängig vom Scope) oder alternativ die normale Login-Session des Browsers; anders als bei /verify und /invoices entscheidet keine OAuth-Scope-Prüfung, sondern ausschließlich das gebuchte Feature des Kontos.

FeatureSchaltet freiKommt mit
converter/verify, /invoicesjedem aktiven Abo (8 € zzgl. MwSt./Monat oder höher)
payments/payments/sepa, /payments/bank-lookupdem Zahlungsverkehr-Abo (29 € zzgl. MwSt./Monat, schließt converter ein)
paymentsAbroad/payments/axz, /payments/structure-addresses, /payments/bank-lookupdem Auslandsmodul-Add-on (19 € zzgl. MwSt./Monat, nur zusammen mit dem Zahlungsverkehr-Abo buchbar, zusammen 48 € zzgl. MwSt./Monat)

GET https://finisma.de/api/v1/me meldet die gebuchten Features eines Kontos im Feld features, siehe Abschnitt „Authentifizierung“ oben.

POST /payments/sepa

Erzeugt aus einem Zahllauf (JSON) die SEPA-Sammelüberweisung (pain.001.001.09, SCT). Es ist dieselbe Erzeugung wie auf /zahlungen, das XML ist byteidentisch. Gedacht für ERP-Anbindungen und Workflows wie „Rechnungen rein, Zahldatei raus“. Erfordert payments. Die Antwort ist standardmäßig das XML als Datei (Content-Disposition, Dateiname sepa-inland-<Datum>.xml).

curl -X POST https://finisma.de/api/v1/payments/sepa \
  -H "Authorization: Bearer $FINISMA_KEY" \
  -H "Content-Type: application/json" \
  -d @zahllauf.json \
  -o sepa-inland.xml

Vor der Erzeugung laufen die Geschäftsregeln der /zahlungen-Seite (IBAN-Prüfziffern, Beträge, Verwendungszweck-Längen, Ausführungsdatum nicht in der Vergangenheit). Ein Verstoß ist ein 422 mit der vollständigen Issue-Liste in details (je Eintrag code, message, path, severity). Eine Datei wird dabei nicht erzeugt: lieber eine klare Absage als eine Datei, die die Bank ablehnt. Nach der Erzeugung prüft ein QA-Gate das XML autoritativ gegen das DK-TVS; scheitert es, kommt 502 mit den Befunden statt der Datei.

Mit Accept: application/json kommt statt des XML die Hülle { xml, filename, summary, warnings, verification }. Es gilt dieselbe enge Regel wie bei /invoices: der Header muss application/json nennen und darf weder application/xml noch */* enthalten. Der Standard-Header vieler HTTP-Bibliotheken bekommt also das XML, nicht ungefragt JSON. warnings sind nicht blockierende Meldungen (etwa die Dopplungs-Warnung); verification sagt, ob die Datei das autoritative QA-Gate durchlaufen hat.

Ein vollständiger Zahllauf mit zwei Zahlungen, die zweite mit eigener End-to-End-Referenz (ohne Angabe steht NOTPROVIDED in der Datei). Als zahllauf.json gespeichert läuft das Beispiel unverändert durch den curl-Aufruf oben:

{
  "debtor": {
    "name": "Muster GmbH",
    "iban": "DE89370400440532013000",
    "bic": "COBADEFFXXX"
  },
  "requestedExecutionDate": "2030-01-15",
  "payments": [
    {
      "creditorName": "Beispiel AG",
      "creditorIban": "DE02120300000000202051",
      "amount": 1190,
      "currency": "EUR",
      "remittanceInfo": "Rechnung 2026-001"
    },
    {
      "creditorName": "Zulieferer GmbH",
      "creditorIban": "DE44500105175407324931",
      "amount": 250.5,
      "currency": "EUR",
      "remittanceInfo": "Rechnung 4711",
      "endToEndId": "RG-4711"
    }
  ]
}

POST /payments/axz

Erzeugt aus einem Zahllauf die Auslandszahlungsdatei nach dem DK-AXZ-Schema (GBIC_5): beliebige ISO-Währungen, Entgeltregelung je Zahlung (chargeBearer, Standard SHAR), Ausführungspriorität über serviceLevel (NURG, URGP, SDVA). Erfordert paymentsAbroad; payments allein reicht nicht. Ablauf, Fehlerformen und Accept-Regel wie bei /payments/sepa, das QA-Gate prüft gegen das AXZ-TVS; Dateiname axz-ausland-<Datum>.xml.

Zwei Angaben verlangt das AXZ-Profil zusätzlich: die Empfängeradresse (creditorAddress mit mindestens Ort und Länderkennung) und die Adresse des Auftraggebers (debtor.address). Statt einer IBAN ist auch eine nationale Kontonummer möglich (creditorAccountOther, dann BIC oder Empfängerbank mit Name und Adresse in creditorAgent).

curl -X POST https://finisma.de/api/v1/payments/axz \
  -H "Authorization: Bearer $FINISMA_KEY" \
  -H "Content-Type: application/json" \
  -d @zahllauf-ausland.json \
  -o axz-ausland.xml
{
  "debtor": {
    "name": "Muster GmbH",
    "iban": "DE89370400440532013000",
    "bic": "COBADEFFXXX",
    "currency": "EUR",
    "address": { "townName": "Hamburg", "countryCode": "DE" }
  },
  "requestedExecutionDate": "2030-01-15",
  "payments": [
    {
      "creditorName": "US Supplies Inc.",
      "creditorIban": "GB29NWBK60161331926819",
      "creditorBic": "NWBKGB2LXXX",
      "amount": 4800,
      "currency": "USD",
      "remittanceInfo": "Invoice INV-2030-017",
      "creditorAddress": {
        "streetName": "5th Avenue",
        "buildingNumber": "100",
        "postCode": "10001",
        "townName": "New York",
        "countryCode": "US"
      }
    }
  ]
}

GET /payments/bank-lookup

Liefert den BIC zu einer deutschen Bankleitzahl aus dem Bundesbank-Verzeichnis. Erfordert payments oder paymentsAbroad.

ParameterBedeutung
countryISO-3166-1-alpha-2-Ländercode. Nur DE liefert einen Treffer; jeder andere Wert liefert { "found": false, "reason": "unsupported_country" } (auch das mit Status 200, kein Fehler).
bankCodeDie achtstellige deutsche Bankleitzahl.
curl "https://finisma.de/api/v1/payments/bank-lookup?country=DE&bankCode=10070000" \
  -H "Authorization: Bearer $FINISMA_KEY"

Antwort bei einem Treffer:

{ "found": true, "bic": "DEUTDEBBXXX", "bankName": "Deutsche Bank", "dataDate": "2026-06-08" }

Kein Treffer für eine unbekannte, aber gültig aufgebaute BLZ:

{ "found": false }

POST /payments/structure-addresses

Zerlegt bis zu 50 Freitext-Adressen (je 1 bis 4 Zeilen) per KI in Ort, Postleitzahl, Straße und Hausnummer, gedacht für Auslandsüberweisungen ohne strukturierte Adressfelder. Erfordert paymentsAbroad (payments allein reicht hier nicht, anders als bei bank-lookup). Ohne Persistenz: Die Adressdaten werden nur für den Aufruf verarbeitet.

curl -X POST https://finisma.de/api/v1/payments/structure-addresses \
  -H "Authorization: Bearer $FINISMA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "id": "1", "countryCode": "FR", "addressLines": ["12 Rue de la Paix", "75002 Paris"] }
    ]
  }'

Antwort, ein Ergebnis pro eingehender ID, in derselben Reihenfolge:

{
  "items": [
    {
      "id": "1",
      "townName": "Paris",
      "postCode": "75002",
      "streetName": "Rue de la Paix",
      "buildingNumber": "12",
      "confident": true
    }
  ]
}

War sich das Modell insgesamt nicht sicher, kommt nur { "id": "1", "confident": false } zurück. Es wird nichts erfunden, auch keine addressLines. Diesen Fall sollte die aufrufende Anwendung dem Nutzer zur manuellen Prüfung vorlegen.

Fehler

Alle Endpoints, auch die Zahlungsverkehr-Endpoints, antworten im selben Format: JSON { "error", "code", "details" } mit passendem HTTP-Status. Für structure-addresses kommen invalid_input (400, Body entspricht nicht dem Schema) und upstream_error (502, der KI-Dienst ist nicht erreichbar) dazu. Die Zahlungsdatei-Endpoints trennen zwei Ablehnungsstufen: 400, wenn der Body nicht dem Schema entspricht, und 422, wenn er dem Schema entspricht, aber Geschäftsregeln verletzt; die Issue-Liste steht dann in details.

StatuscodeBedeutung
400invalid_inputDatei oder Rechnungsdaten fehlerhaft.
422invalid_inputNur /payments/sepa und /payments/axz: der Zahllauf entspricht dem Schema, verletzt aber Geschäftsregeln (z. B. IBAN-Prüfziffer). details enthält die Issue-Liste, eine Datei wird nicht erzeugt.
401auth_required / invalid_keyKein Credential mitgeschickt bzw. ungültig, abgelaufen oder widerrufen.
403subscription_requiredWeder Abo noch Guthaben: beim Auslesen, bei format=xml und beim Erzeugen erst, wenn auch Guthaben (und beim Erzeugen das Tageskontingent) nicht greifen.
429free_quota_exhaustedTageslimit der kostenlosen Erzeugung erreicht; Wartezeit bis Mitternacht im Header Retry-After.
403insufficient_scopeOAuth-Token ohne den nötigen Scope für diese Funktion.
429rate_limitedZu viele Anfragen; Wartezeit im Header Retry-After.
502upstream_errorFehler in einem nachgelagerten Dienst. Bei den Zahlungsdatei-Endpoints auch: das erzeugte XML hat das autoritative QA-Gate nicht bestanden (details enthält die Befunde); es wird keine ungeprüfte Datei ausgeliefert.

Hinweise

  • Die API ist für Server-zu-Server-Aufrufe gedacht. Es werden keine CORS-Header gesetzt, Aufrufe direkt aus dem Browser sind nicht vorgesehen.
  • Hochgeladene Dateien werden nur für die Verarbeitung gehalten und nicht dauerhaft gespeichert.
  • Automatisierungen wie n8n nutzen den invoice-Teil als JSON und den pdf-Teil als Binär-Datei im selben multipart-Request.

Maschinenlesbare Spezifikation: https://finisma.de/api/v1/openapi.json