Zum Inhalt springen
RechnungslotseRechnungslotseRechnungslotseRechnungslotse

Schnittstelle

E-Rechnungen aus deinem System – über eine Schnittstelle, die zurückredet

Dein Shop schickt Empfänger und Positionen, Rechnungslotse gibt eine geprüfte E-Rechnung zurück: XRechnung, PDF und ZUGFeRD, mit einer Nummer aus dem Nummernkreis des Betriebs und derselben Prüfung nach EN 16931 wie im Portal. Wiederholte Aufrufe sind eingeplant, Fehler tragen ihren Grund mit.

Sie kostet nichts extra – bezahlt wird die Menge

Die Schnittstelle steht in jedem Tarif offen, auch im kostenlosen Konto. Kein Aufpreis je Aufruf, keine Gebühr je Bestellung. Was zählt, ist die Zahl der erzeugten Rechnungen – und die läuft gegen dasselbe Monatskontingent wie die im Portal. Ein Konto brauchst du trotzdem: Ein Schlüssel entsteht nur dort.

Kostenloses Konto

3 / Monat

Zum Ausprobieren: einmal durchlaufen lassen, die drei Dateien ansehen, dann entscheiden.

Pro

empfohlen

200 / Monat

Der Regelfall für einen laufenden Shop. Deckt, was ein Betrieb üblicherweise im Monat schreibt.

Büro

ohne Grenze

Für Handel mit hohem Belegaufkommen. Es bleibt nur die Stundengrenze gegen Massenabrufe.

Für einen laufenden Shop ist das kostenlose Konto zu klein – drei Rechnungen sind zum Ausprobieren gedacht, nicht zum Betrieb. Wer die Anbindung produktiv nutzt, fährt ab Pro richtig.

Neu

Dieselben Fähigkeiten gibt es auch als MCP-Server für Claude und ChatGPT – prüfen und lesen ohne Konto, erstellen mit deinem Zugangsschlüssel. Gleiche Engine, gleiches Kontingent, gleiche Idempotenz.

Anmelden

Jeder Zugang gehört zu genau einem Betrieb und trägt nur die Rechte, die du ihm gibst. Der Schlüssel gehört in die Kopfzeile – nie in die Adresse: Dort landet er im Zugriffsprotokoll jedes Proxys und ist nicht mehr geheim. Aufrufe mit einem Schlüssel in der Adresse weisen wir deshalb ausdrücklich ab.

Authorization: Bearer rl_live_…

Endpunkte

Verfügbare Endpunkte mit benötigtem Recht, Zweck und Antwort
AufrufRechtZweck
POST/api/v1/rechnungenschreiben
Erzeugt eine Rechnung aus Empfänger und Positionen. Absenderangaben, Nummer und Zahlungsziel kommen aus dem Betrieb – der Aufruf liefert sie nicht mit. Pflicht ist `lines`; `buyer`, `buyerReference`, `note`, `paymentTerms` und `issueDate` (Vorgabe heute) sind optional. Idempotent über `Idempotency-Key`. Eine Anlage dauert rund sechs Sekunden (Prüfung, PDF, ZUGFeRD); der Prüfdienst nimmt drei gleichzeitig, weitere warten bis 20 s und bekommen sonst 503 mit `Retry-After` – dann später wiederholen, mit demselben Key.201 mit Nummer, Beträgen und den Abhol-Adressen der drei Dateien.
GET/api/v1/rechnungenlesen
Listet die Belege des Betriebs, neueste zuerst. Filter: `status` (draft, open, paid, overdue, cancelled), `von` und `bis` über das Rechnungsdatum, `suche` in Nummer und Empfängername, `limit` (1–100, Vorgabe 25). Mit `faellig=1` kommen stattdessen die überfälligen, älteste zuerst.200 mit `invoices` – Nummer, Zustand, Datum, Fälligkeit, Brutto, bereits gezahlt, offener Rest und `overdue` (kalendarisch gerechnet, nicht am gespeicherten Zustand). Bei `faellig=1` zusätzlich `daysOverdue`, die bereits verschickten Mahnstufen und die nächste fällige.
GET/api/v1/rechnungen/{id}/{format}lesen
Holt eine erzeugte Datei. Format ist `xrechnung`, `pdf` oder `zugferd`.Die Datei als Download, sonst 404.
POST/api/v1/rechnungen/{id}/zahlungschreiben
Bucht einen Zahlungseingang. `amountCents` ganzzahlig, ohne Angabe gilt der offene Rest; `date` als `JJJJ-MM-TT`, ohne Angabe heute. Mehr als der offene Rest wird abgewiesen (400 `zahlung_zu_hoch`) und nicht stillschweigend gekappt. Idempotent über `Idempotency-Key` – sonst stünde die Zahlung nach einem Netzabbruch zweimal im Journal.200 mit gebuchtem Betrag, neuem Zahlstand, offenem Rest und `settled`. Eine stornierte Rechnung antwortet 409, eine fremde 404.
POST/api/v1/rechnungen/{id}/stornoschreiben
Storniert eine Rechnung vollständig: Es entsteht eine neue Rechnung vom Typ 384 mit negativen Mengen, eigener Nummer und Bezug auf die ursprüngliche (BT-25), mit PDF, XRechnung und ZUGFeRD – nichts wird gelöscht. Kein Kontingentverbrauch. Für eine TEILerstattung stattdessen eine Gutschrift über `POST /rechnungen` mit negativen Mengen erzeugen, denn die Beträge je Position kennt nur der Shop. Idempotent über `Idempotency-Key`; ohne Key schützt der Zustand: Ein zweiter Storno derselben Rechnung antwortet 409 `already_cancelled`.201 mit `id`, `number` des Stornos, `originalNumber`, `issueDate`, `releasedServices` (wieder abrechenbare Leistungen) und den drei Dateiadressen wie beim Erzeugen. Eine fremde Rechnung antwortet 404, eine bereits stornierte 409, ein Storno eines Stornos ebenfalls 409.
POST/api/v1/pruefenpruefen
Prüft eine XRechnung (UBL oder CII) oder eine ZUGFeRD-PDF gegen EN 16931 – derselbe Prüfer, durch den jede von uns erzeugte Datei läuft. Entweder `multipart/form-data` mit Feld `file` oder der rohe Dateiinhalt mit `Content-Type: application/xml` bzw. `application/pdf`, höchstens 5 MB. Der Dateityp wird aus den Bytes bestimmt, nicht aus dem Namen. Kein Kontingentverbrauch, nur die Ratengrenze je Schlüssel.200 mit `valid`, `profile`, `format` (ubl, cii, pdf), `summary` (errors, warnings, notices) und `messages` je Regel mit `severity`, `code`, `location` und `text`. Eine Datei, die durchfällt, ist KEIN Fehler: 200 mit `valid: false`. 400 nur, wenn die Datei weder XML noch PDF ist, 413 über 5 MB, 503 wenn der Prüfdienst nicht antwortet.
GET/api/v1/kundenlesen
Sucht in der Kundenkartei: `suche` trifft Name, Ort und E-Mail, `limit` (1–100, Vorgabe 20). Kunden hängen am KONTO, nicht am Betrieb – dieselbe Firma wird von beiden Betrieben eines Büros beliefert. Archivierte bleiben aussen vor.200 mit `customers`: Anschrift, Ansprechpartner, E-Mail, USt-IdNr. und Leitweg-ID (`buyerReference`). Genau die Felder, die `POST /api/v1/rechnungen` als `buyer` erwartet.
GET/api/v1/leistungenlesen
Liest die noch nicht abgerechneten Leistungen. Filter `kunde_id`, `von`, `bis`, `limit` (1–500, Vorgabe 100). Mit `gruppiert=1` kommt stattdessen die Summe je Kunde – das ist die Zahl, aus der die Monatsrechnung entsteht, und sie rundet je Steuergruppe wie der Beleg selbst.200 mit `services` (Datum, Kunde, Bezeichnung, `quantityMilli`, `unitCode`, `unitPriceCents`, Steuersatz) und den Summen; `truncated` sagt, ob am `limit` abgeschnitten wurde – dann gelten die Summen nur für die gelieferten Zeilen. Mit `gruppiert=1`: `customers` mit Netto und Brutto je Kunde.
POST/api/v1/leistungenschreiben
Erfasst eine Leistung – Stunden, Material oder Pauschale. Pflicht: `kunde` (Name) oder `kunde_id`, `datum` als `JJJJ-MM-TT`, `bezeichnung` und `menge` als Dezimalzahl (1,5 statt 1500). `einheit` als deutsches Wort, Vorgabe Stunde; `einzelpreis` in Euro und `ust_prozent` kommen aus `artikel`, wenn du einen nennst, und mitgeschickte Werte gewinnen. Ein unbekannter Kunde wird NICHT angelegt, sondern abgewiesen (404 `kunde_unbekannt`) – sonst füllt sich die Kartei mit Schreibweisen derselben Firma. Idempotent über `Idempotency-Key`.201 mit Kennung, Kunde, Menge in Tausendsteln, Einheit, Einzelpreis und Nettobetrag in Cent.
POST/api/v1/leistungen/abrechnenschreiben
Macht aus offenen Leistungen EINES Kunden eine Rechnung. Entweder `leistung_ids` genau benennen oder `kunde_id` mit `von`/`bis` – dann wird alles Offene im Zeitraum genommen. `verdichten` ist an, gleiche Leistungen werden zu einer Position zusammengefasst; `datum` setzt das Rechnungsdatum. Abgerechnete Leistungen sind gesperrt, ein zweiter Lauf über dieselbe Stunde ist nicht möglich (409 `leistungen_leer`, wenn nichts offen ist). Idempotent über `Idempotency-Key`.201 mit Nummer, Bruttobetrag, Anzahl der abgerechneten Leistungen und den Abhol-Adressen der Dateien. Der Stapel über ALLE Kunden gehört bewusst nicht hierher, sondern ins Portal.
GET/api/v1/betrieblesen
Liest die Stammdaten des Betriebs, an dem der Schlüssel hängt.200 mit Anschrift, Steuernummer, Bankverbindung und Gestaltung.
PATCH/api/v1/betriebstammdaten
Ändert einzelne Felder des Betriebs. Nur mitgeschickte Felder werden angefasst – ein nicht genanntes Feld bleibt, wie es ist.200 mit dem neuen Stand.
GET/api/v1/umsatzsteuerlesen
Die Zahlen für die Umsatzsteuer-Voranmeldung eines Zeitraums. `von` und `bis` sind Pflicht (`JJJJ-MM-TT`). Soll oder Ist entscheidet der Betrieb über `versteuerung` und nicht der Aufruf – ein Parameter dafür wäre die Einladung, sich die günstigere Zahl auszusuchen.200 mit einer Zeile je ELSTER-Kennziffer (Netto und Steuer in Cent), den Summen und der Zahl der berücksichtigten Belege. Die Aufstellung stammt nur aus Ausgangsrechnungen: keine Vorsteuer, kein § 13b als Leistungsempfänger, keine Korrekturen aus Vormonaten. Der Hinweis steht als `disclaimer` in der Antwort und gehört mit angezeigt.

Webhooks: Rechnungslotse meldet sich

Statt zu fragen, ob eine Zahlung eingegangen ist, hinterlegst du unter Einstellungen → Schnittstelle eine Adresse. Jede Zustellung ist ein POST mit JSON und der Kopfzeile X-Rechnungslotse-Signature: t=<unix>,v1=<hex>– HMAC-SHA256 über Zeitstempel, Punkt und rohen Rumpf. Antwortet dein System nicht mit 2xx, wiederholen wir nach 1 min, 5 min, 30 min, 2 h und 12 h; danach ist die Zustellung aufgegeben, und nach fünf Fehlschlägen in Folge ist der Webhook gesperrt, bis du ihn bewusst wieder einschaltest.

EreignisWanndata enthält
invoice.createdEine Rechnung ist erzeugt – im Portal, über die Schnittstelle oder aus einer Serie.`id`, `number`, `typeCode`, `grossTotalCents`
invoice.paidEine Rechnung ist vollständig bezahlt.`id`, `number`, `paidAmountCents`
invoice.paymentEine Teilzahlung ist gebucht.`id`, `number`, `bookedCents`, `openAmountCents`
invoice.cancelledEine Rechnung ist storniert; die Stornorechnung (Typ 384) existiert.`id` und `number` des Stornos, `originalId`, `originalNumber`
// Node.js – Signatur prüfen, Zustellungen älter als 5 Minuten verwerfen
const [t, v1] = req.headers["x-rechnungslotse-signature"].split(",").map((s) => s.split("=")[1]);
const erwartet = crypto.createHmac("sha256", SECRET).update(t + "." + rawBody).digest("hex");
const ok = Math.abs(Date.now() / 1000 - Number(t)) < 300
  && crypto.timingSafeEqual(Buffer.from(erwartet, "hex"), Buffer.from(v1, "hex"));

Die vollständige Beschreibung inklusive der Webhook-Schemata liegt maschinenlesbar unter /api/v1/openapi.json(OpenAPI 3.1).

Ein vollständiger Aufruf

So wie er ist, funktioniert er. Absender, Nummer und Fälligkeit stehen bewusst nicht darin – die kommen aus dem Betrieb.

Anfrage

curl -X POST https://rechnungslotse.de/api/v1/rechnungen \
  -H "Authorization: Bearer rl_live_DEIN_SCHLUESSEL" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: bestellung-4711" \
  -d '{
    "buyer": {
      "name": "Beispiel GmbH",
      "street": "Musterweg 3",
      "zip": "10115",
      "city": "Berlin",
      "email": "rechnung@beispiel.de"
    },
    "lines": [
      {
        "name": "Artikel A",
        "quantityMilli": 2000,
        "unitCode": "C62",
        "unitPriceCents": 4900,
        "taxCategory": "S",
        "taxRatePercentMilli": 19000
      }
    ],
    "buyerReference": "SHOP-4711"
  }'

Antwort · 201

{
  "id": "8ea593ca-4b43-4d69-a8a3-8a4087515a03",
  "number": "RE-2026-0003",
  "issueDate": "2026-07-31",
  "dueDate": "2026-08-14",
  "grossTotalCents": 11662,
  "documents": [
    { "format": "xrechnung", "filename": "RE-2026-0003.xml", "url": "…" },
    { "format": "pdf",       "filename": "RE-2026-0003.pdf", "url": "…" },
    { "format": "zugferd",   "filename": "RE-2026-0003_zugferd.pdf", "url": "…" }
  ]
}

Die Positionszeile

Hier geht die meiste Zeit verloren, deshalb ausführlich: Mengen und Steuersätze sind ganzzahlig in Tausendsteln, Beträge in Cent. Das ist unbequemer zu tippen und die einzige Art, bei der nichts gerundet wird.

Felder einer Position mit Pflichtangabe und Erklärung
FeldPflichtBedeutung
nameja
Bezeichnung der Leistung, wie sie auf der Rechnung steht.
quantityMillija
Menge in TAUSENDSTELN. 2 Stück sind 2000, eine halbe Stunde ist 500. Ganzzahlig, damit nichts gerundet wird.
unitCodeja
Einheit nach UN/ECE Rec. 20: C62 (Stück), HUR (Stunde), DAY (Tag), MTR (Meter), MTK (m²), KGM (kg), LTR (Liter), KMT (km), H87 (Stück, alternativ).
unitPriceCentsja
Einzelpreis NETTO in Cent. Ganzzahlig, nie negativ – eine Gutschrift läuft über eine negative Menge.
taxCategoryja
Steuerkategorie: S (Regelsatz), Z (Nullsatz), E (befreit), AE (Reverse Charge), K (innergemeinschaftlich), G (Ausfuhr), O (nicht steuerbar).
taxRatePercentMillija
Steuersatz in Tausendstel Prozent: 19 % sind 19000, 7 % sind 7000.
descriptionoptional
Zusatztext unter der Bezeichnung.
allowanceCentsoptional
Nachlass auf diese Position, in Cent.
chargeCentsoptional
Zuschlag auf diese Position, in Cent.

Wenn etwas schiefgeht

Jede Fehlerantwort trägt einen code, eine Meldung in Klartext und – wo es hilft – eine Liste checks mit den betroffenen Feldern. Die letzte Spalte ist die wichtigste: Sie sagt, ob eine Wiederholung Sinn hat.

Fehlercodes mit Status, Bedeutung und Handlungsempfehlung
CodeHTTPBedeutetWas tun
invalid_input400Der Datensatz ist unvollständig oder widersprüchlich. Die Antwort trägt `checks` mit den betroffenen Feldern.Nicht wiederholen – die Angaben korrigieren. Die Liste nennt das Feld.
validation_failed422Der Datensatz war vollständig, aber das Ergebnis besteht die Prüfung nach EN 16931 nicht. `checks` trägt die Regelmeldungen.Nicht wiederholen. Meist fehlt eine Pflichtangabe am Betrieb (Telefon, Bankverbindung).
api_schluessel_ungueltig401Unbekannter oder gesperrter Schlüssel.Nicht wiederholen. Im Portal einen neuen Zugang anlegen.
api_schluessel_abgelaufen401Der Schlüssel hatte ein Ablaufdatum, und das ist überschritten.Nicht wiederholen. Neuen Zugang anlegen und im Plugin hinterlegen.
api_recht_fehlt403Der Schlüssel ist gültig, trägt dieses Recht aber nicht.Nicht wiederholen. Im Portal einen Zugang mit dem passenden Recht anlegen.
quota_exhausted429Das Monatskontingent des Tarifs ist aufgebraucht.Später wiederholen – im nächsten Monat, oder wenn der Betrieb aufstockt.
rate_limited429Zu viele Aufrufe in kurzer Zeit.Wiederholen, mit wachsendem Abstand (exponentiell).
service_unavailable503Ein nachgelagerter Dienst antwortet gerade nicht.Wiederholen. MIT demselben Idempotency-Key, dann entsteht keine zweite Rechnung.

Fertige Plugins für WooCommerce und Shopify

Sie sind in Arbeit und benutzen genau diese Schnittstelle – nichts Eigenes, nichts Verstecktes. Wer heute schon anbinden will, kommt mit dieser Seite und einem Zugang aus dem Portal aus. Sag uns Bescheid, wenn du etwas brauchst, das hier fehlt: kontakt@rechnungslotse.de

Häufige Fragen

Was kostet die Schnittstelle?

Sie steht in jedem Tarif offen und kostet keinen Aufpreis – auch nicht je Aufruf. Bezahlt wird die Menge: Im kostenlosen Konto sind drei Rechnungen im Monat drin, in Pro 200, ab Büro gibt es keine Mengengrenze. Erzeugte Rechnungen zählen gegen dasselbe Kontingent wie die im Portal – die Schnittstelle ist eine zweite Tür zur selben Engine, keine Umgehung.

Wie verhindere ich doppelte Rechnungen bei einem wiederholten Webhook?

Schick einen Idempotency-Key mit, zum Beispiel die Bestellnummer deines Shops. Ein zweiter Aufruf mit demselben Schlüssel gibt die erste Antwort zurück – gleiche Rechnungsnummer, keine zweite Rechnung, keine Lücke im Nummernkreis. Ohne den Kopf legen wir beide Aufrufe an, weil zwei gleich aussehende Bestellungen am selben Tag normal sind.

Warum kann ich die Absenderangaben nicht mitschicken?

Weil sie sonst zweimal existieren. Anschrift, Steuernummer und Bankverbindung stehen am Betrieb und kommen bei jeder Erzeugung frisch von dort. Liefe eine zweite Fassung im Plugin mit, stünde auf der Rechnung irgendwann etwas anderes als im Portal – und das sind Pflichtangaben nach § 14 UStG, an denen der Vorsteuerabzug des Empfängers hängt.

Kann ein Schlüssel ablaufen?

Auf Wunsch. Beim Anlegen lässt sich 90 Tage, ein Jahr oder zwei Jahre wählen; ohne Angabe gilt er unbefristet. Ein abgelaufener Schlüssel antwortet mit einer eigenen Meldung, damit im Plugin erkennbar ist, warum es stehenbleibt.

Gibt es fertige Plugins?

Für WooCommerce und Shopify sind sie in Arbeit. Die Schnittstelle darunter ist dieselbe – wer nicht warten will, baut mit dieser Seite in einem Nachmittag seine eigene Anbindung.

Dürfen wir Cookies für die Messung setzen?

Wie viele Menschen die Seite lesen, zählen wir mit unserer eigenen Matomo-Instanz auf einem Server in Deutschland – ohne Cookies und ohne etwas von deinem Gerät abzufragen. Mit deiner Zustimmung darf Matomo zusätzlich einen Cookie setzen. Dann sehen wir, ob jemand wiederkommt, und wo Leute abbrechen. Nur das. Die Daten gehen an niemanden sonst, und die Seite funktioniert in beiden Fällen vollständig.

Ändern kannst du das jederzeit in der Fußzeile unter „Cookie-Einstellungen“ – dort lässt sich die Messung auch ganz abschalten. Einzelheiten stehen in der Datenschutzerklärung.