Rechnungswerk

API-Dokumentation

Mit der Schnittstelle übergibt Ihr System eine Rechnung an Rechnungswerk. Rechnungswerk vergibt die Rechnungsnummer, erzeugt ZUGFeRD-PDF und XRechnung, prüft beide und versendet sie auf Wunsch per E-Mail.

Basisadresse
https://api.erechnung2027.entgema.com/v1
Alternativ
https://erechnung2027.entgema.com/api/v1
Anmeldung
Authorization: Bearer <Schlüssel>
Format
JSON, UTF-8

Schnellstart

  1. Schlüssel besorgen

    Sie erhalten Ihren API-Schlüssel vom Betreiber. Er beginnt mit rw_ und wird nur einmal angezeigt.

  2. Rechnung als JSON aufbauen

    Kunde, Positionen und Leistungszeitraum – siehe Rechnung einliefern.

  3. Einliefern

    curl -X POST https://api.erechnung2027.entgema.com/v1/submit \
      -H "Authorization: Bearer rw_IHR_SCHLUESSEL" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: auftrag-4711" \
      -d @rechnung.json
  4. Antwort auswerten

    Sie erhalten Rechnungsnummer, Beträge, Adressen der Dateien und den Stand des Versands.

Zugang und Schlüssel

Jeder Aufruf trägt den Schlüssel im Kopf Authorization: Bearer rw_…. Ein Schlüssel hat eine oder mehrere Berechtigungen:

BerechtigungErlaubt
readLesen von Rechnungen, Kunden und Dateien (immer enthalten)
writeKunden und Rechnungsentwürfe anlegen
issueRechnungen verbindlich ausstellen und stornieren
email.sendE-Mails versenden

Behandeln Sie den Schlüssel wie ein Passwort: nur serverseitig verwenden, nie in einer App oder im Browser ausliefern. Bei Verdacht auf Missbrauch lässt ihn der Betreiber sofort widerrufen.

Grundregeln

Wiederholungen sind sicher
Jeder POST braucht den Kopf Idempotency-Key (8–128 Zeichen aus Buchstaben, Ziffern und _ . : -). Derselbe Schlüssel mit demselben Inhalt liefert das ursprüngliche Ergebnis und legt nichts doppelt an. Verwenden Sie z. B. Ihre Auftragsnummer.
Beträge als Text
Mengen und Preise als Zeichenkette mit Punkt senden: "120.00", "2.5". Ganze Zahlen werden angenommen, JSON-Gleitkommazahlen abgelehnt – so entstehen keine Rundungsfehler.
Rechnen übernimmt Rechnungswerk
Sie senden Menge, Nettopreis, Rabatt und Steuersatz. Netto, Umsatzsteuer und Brutto berechnet der Server und gibt sie zurück.
Datum
Immer JJJJ-MM-TT.
Umfang
Kunden in Deutschland, Währung Euro, Regelbesteuerung mit 19 % oder 7 %.
Ausgestellt heißt unveränderlich
Eine ausgestellte Rechnung lässt sich nicht mehr ändern, nur stornieren.

Rechnung einliefern

POSThttps://api.erechnung2027.entgema.com/v1/submit

Ein Aufruf erledigt alles: Kunde finden oder anlegen, Rechnung anlegen und ausstellen, Dateien erzeugen, prüfen und – mit "send": true – versenden. Benötigt write, zum Ausstellen issue, zum Senden email.send.

Anfrage

{
  "customer": {
    "name": "Beispielkunde GmbH",
    "street": "Testweg 5",
    "postcode": "20095",
    "city": "Hamburg",
    "email": "buchhaltung@beispielkunde.de",
    "buyer_reference": "AUFTRAG-4711"
  },
  "invoice": {
    "issue_date": "2026-10-04",
    "service_start": "2026-09-01",
    "service_end": "2026-09-30",
    "lines": [
      {"name": "Beratung", "description": "Analyse und Konzept", "quantity": "2.5", "price": "120.00", "unit": "HUR", "vat": "19"}
    ]
  },
  "send": true
}
FeldPflichtBedeutung
customer_ideines von beidenNummer oder UUID eines vorhandenen Kunden
customereines von beidenKundendaten: name, street, postcode, city, email (Pflicht), dazu vat_id, contact, phone, buyer_reference. Ein Kunde mit gleicher E-Mail und gleichem Namen wird wiederverwendet.
invoice.linesjaPositionen, mindestens eine – siehe unten
invoice.issue_dateneinRechnungsdatum, Standard: heute
invoice.service_start, service_endneinLeistungszeitraum, Standard: Rechnungsdatum
invoice.due_dateneinFälligkeit, Standard: Rechnungsdatum + 14 Tage
invoice.buyer_referencesiehe TextKäuferreferenz bzw. Leitweg-ID. Fehlt sie hier und beim Kunden, kann die Rechnung nicht ausgestellt werden.
invoice.terms, invoice.noteneinZahlungsbedingungen, Hinweistext
issueneinStandard true. Mit false entsteht nur ein Entwurf.
sendneinStandard false. Mit true wird sofort versendet.
emailneinAbweichend von der Vorlage: recipient, subject, body, selection (["zugferd","xrechnung"])

Position

FeldPflichtBedeutung
namejaBezeichnung, höchstens 200 Zeichen
descriptionneinBeschreibung, Absätze bleiben erhalten
quantityneinMenge, bis vier Nachkommastellen, Standard "1"
pricejaEinzelpreis netto in Euro, bis vier Nachkommastellen
discountneinRabatt in Prozent, 0 bis 100
unitneinC62 Stück (Standard), HUR Stunde, DAY Tag, MON Monat, MTR Meter, KGM Kilogramm, LTR Liter
vatnein"19" (Standard), "7" oder "0" (steuerfrei). Bei "0" braucht die Rechnung das Feld exemption_reason, z. B. „Steuerfrei nach § 4 Nr. 21 UStG“. Ist der Betreiber Kleinunternehmer (§ 19 UStG), wird jede Position ohne Umsatzsteuer berechnet.

Antwort 201

{
  "invoice": {
    "id": 12, "uuid": "9f1c…", "number": "RE-2026-000012", "status": "issued",
    "issue_date": "2026-10-04", "due_date": "2026-10-18",
    "net": "300.00", "tax": "57.00", "gross": "357.00", "currency": "EUR",
    "payment_status": "unpaid", "paid": "0.00",
    "url": "https://api.erechnung2027.entgema.com/v1/invoices/12"
  },
  "files": {
    "zugferd":     {"url": "https://api.erechnung2027.entgema.com/v1/invoices/12/files/zugferd", "sha256": "…", "size": 48211},
    "xrechnung":   {"url": "https://api.erechnung2027.entgema.com/v1/invoices/12/files/xrechnung", "sha256": "…", "size": 6120},
    "zugferd_xml": {"url": "https://api.erechnung2027.entgema.com/v1/invoices/12/files/zugferd_xml", "sha256": "…", "size": 5980}
  },
  "validation": {"status": "unvalidated", "internal_passed": true, "message": "Interne Prüfung bestanden: …", "errors": []},
  "sendable": true,
  "mail_mode": "smtp",
  "customer": {"id": 3, "created": true},
  "dispatch": {
    "status": "sent", "email_id": 20, "recipient": "buchhaltung@beispielkunde.de",
    "subject": "Ihre Rechnung RE-2026-000012", "sent_at": "2026-10-04T00:50:00+02:00",
    "already_sent": false, "reason": null
  }
}

Stand des Versands

dispatch.statusBedeutung
sentDer Mailserver hat die Nachricht angenommen. Das ist keine Zustellbestätigung.
test_modeTestmodus des Betreibers: Die Nachricht wurde nur gespeichert, nicht versendet.
blockedVersand gesperrt, weil die Prüfung nicht bestanden oder die Freigabe nicht erteilt ist. Grund in reason.
failedFehler vor der Übergabe an den Mailserver; es ging nichts hinaus. Grund in reason.
queuedFreigegeben, wird in Kürze versendet.
unknownErgebnis unklar. Nicht automatisch wiederholen, sondern nachfragen.

Scheitert das Ausstellen, etwa wegen fehlender Käuferreferenz, antwortet die Schnittstelle mit einem Fehler – und es bleibt weder ein Entwurf noch ein neu angelegter Kunde zurück.

Beispiel in PHP

<?php
$ch = curl_init('https://api.erechnung2027.entgema.com/v1/submit');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
        'Idempotency-Key: auftrag-' . $orderId,   // je Auftrag gleich, damit nichts doppelt entsteht
    ],
    CURLOPT_POSTFIELDS => json_encode($rechnung, JSON_UNESCAPED_UNICODE),
]);
$antwort = json_decode(curl_exec($ch), true);
$status  = curl_getinfo($ch, CURLINFO_HTTP_CODE);   // 201 = angenommen

if ($status === 201) {
    echo $antwort['invoice']['number'];            // z. B. RE-2026-000012
    echo $antwort['dispatch']['status'] ?? '';     // sent | test_mode | blocked | failed
} else {
    echo $antwort['error'];
}

Versand anstoßen

POSThttps://api.erechnung2027.entgema.com/v1/invoices/{id}/dispatch

Versendet eine bereits ausgestellte Rechnung. {id} ist die Nummer oder UUID aus der Antwort der Einlieferung. Benötigt email.send.

{
  "recipient": "andere.adresse@beispielkunde.de",
  "subject": "Ihre Rechnung",
  "body": "Guten Tag, …",
  "selection": ["zugferd", "xrechnung"],
  "resend": false
}

Alle Felder sind freiwillig; ohne Angaben gelten die Vorlage und die Adresse des Kunden. Ging an denselben Empfänger bereits etwas hinaus, wird nicht erneut gesendet und die Antwort enthält "already_sent": true. Mit "resend": true erzwingen Sie einen zweiten Versand.

Stand abfragen

GEThttps://api.erechnung2027.entgema.com/v1/invoices/{id}/summary

Liefert denselben kompakten Aufbau wie die Einlieferung: Nummer, Beträge, Dateien, Prüfung und Zahlungsstand (invoice.payment_status: unpaid, partial, paid).

Dateien abholen

GEThttps://api.erechnung2027.entgema.com/v1/invoices/{id}/files/{format}

{format}Inhalt
zugferdZUGFeRD-2.5-PDF (PDF/A-3 mit eingebetteter XML)
xrechnungXRechnung 3.0.2 als XML (CII)
zugferd_xmldie im PDF eingebettete XML als eigene Datei

Die Antwort der Einlieferung nennt zu jeder Datei Größe und SHA-256, damit Sie den Abruf prüfen können.

E-Rechnung prüfen und lesen

POSThttps://api.erechnung2027.entgema.com/v1/inspect

Prüft eine beliebige E-Rechnung – XRechnung oder EN 16931 als UBL oder CII, ZUGFeRD-/Factur-X-PDF – und liefert das Prüfergebnis sowie den Inhalt als JSON. Die Datei wird nicht gespeichert und keiner Rechnung zugeordnet. Berechtigung read, kein Idempotency-Key nötig, höchstens 10 MB.

curl -X POST "https://api.erechnung2027.entgema.com/v1/inspect?filename=rechnung.xml" \
  -H "Authorization: Bearer rw_IHR_SCHLUESSEL" \
  --data-binary @rechnung.xml

Alternativ als Formular-Upload im Feld file (multipart/form-data). Die Antwort enthält validation (passed, errors, warnings mit Regelkennung wie BR-CO-15), format (Syntax, Profil) und invoice (Verkäufer, Käufer, Positionen, Steuern, Summen, Zahlung). Es ist eine Teilprüfung; die Referenzprüfung mit KoSIT-Validator und veraPDF ersetzt sie nicht. Dieselbe Prüfung steht ohne Schlüssel im Browser unter https://erechnung2027.entgema.com/pruefen bereit.

Weitere Aufrufe

AufrufZweck
GET /invoices?q=&status=&customer=&page=Rechnungen suchen und blättern (25 je Seite)
GET /invoices/{id}vollständige Rechnung mit allen Positionen
GET /invoices/{id}/emailsKorrespondenz zur Rechnung
POST /invoices/{id}/paymentsZahlung buchen: amount, paid_on, reference
POST /invoices/{id}/cancelStornoentwurf anlegen; danach POST /invoices/{neue-id}/issue
POST /customers, GET/PUT /customers/{id}Kunden anlegen, lesen, ändern
GET /customers/{id}/correspondencegesamte Korrespondenz mit einem Kunden
GET /stats?year=JJJJJahresauswertung
POST /quotes, GET/PUT/DELETE /quotes/{id}Angebot als Entwurf anlegen, lesen, ändern, löschen: customer_id, issue_date, valid_until, lines, terms, note
POST /quotes/{id}/finalizeAngebotsnummer vergeben und Stand festhalten; danach GET /quotes/{id}/pdf
POST /quotes/{id}/sendAngebot per E-Mail senden: recipient, subject, body (Vorgaben über GET /quotes/{id}/mail); Berechtigung email.send
POST /quotes/{id}/status, POST /quotes/{id}/invoiceStatus setzen (accepted, declined, open, draft); Angebot in einen Rechnungsentwurf übernehmen
GET /remindersüberfällige Rechnungen mit Mahnstand und nächster Stufe
POST /invoices/{id}/reminders, POST /reminders/{id}/sendnächste Mahnstufe anlegen; Mahnschreiben senden (GET /reminders/{id}/pdf liefert das PDF)
GET/POST /recurring, PUT/DELETE /recurring/{id}Serien wiederkehrender Rechnungen: invoice_id (Vorlage), name, interval, next_date, period, due_days, mode, end_date; Berechtigung issue
POST /recurring/{id}/run, /pause, /resumeSerie sofort ausführen, anhalten, fortsetzen

Fehler

Fehlerantworten haben immer die Form {"error": "…"}. Der Text nennt die Ursache und, wo möglich, das betroffene Feld.

CodeBedeutungWas tun
400Idempotency-Key fehlt oder ungültig, JSON nicht lesbarAnfrage korrigieren
401Schlüssel fehlt oder ist ungültigKopf Authorization prüfen
403Schlüssel hat die Berechtigung nichtBetreiber um Erweiterung bitten
404Rechnung, Kunde oder Datei nicht gefundenNummer prüfen
409Idempotency-Key mit anderem Inhalt wiederverwendet; Dokument bereits ausgestelltneuen Schlüssel verwenden bzw. Stand abfragen
422Angaben fehlen oder sind ungültiggenanntes Feld korrigieren
429zu viele Aufrufe pro Minuteeine Minute warten
5xxStörung auf dem Servermit demselben Idempotency-Key wiederholen