Zum Hauptinhalt springen
Public API v1

Entwickler & API

epago ist doppelte Buchführung mit DATEV-Kontenrahmen und GoBD-Regeln als Datenbank-Trigger. Die Public API v1 ist kein zweiter, laxerer Weg daneben: jeder Schreibzugriff läuft durch dieselbe Prüfschicht wie die Oberfläche — Soll gleich Haben, Steuerautomatik, Periodensperre, Storno statt Löschen. Zugang bekommst du über einen API-Schlüssel mit Scope read oder write, in zwei Umgebungen: einer Sandbox zum Ausprobieren und dem Live-Zugang zu deiner echten Buchhaltung.

Zuerst: die Sandbox

Bevor du gegen deine echte Buchhaltung testest, nimm die Sandbox. Jeder Account bekommt einen eigenen Test-Mandanten mit einem kleinen Startbestand — Kunden, einen Lieferanten, Rechnungen und Buchungen — unabhängig von deinen echten Daten. Ein Sandbox-Schlüssel beginnt mit epago_test_ und erreicht ausschließlich diesen Test-Mandanten, nie deine echte Buchhaltung.

  • Anlegen und Zurücksetzen im Tab API-Schlüssel unter Mein Konto in der App.
  • Zurücksetzen erzeugt einen frischen Test-Mandanten mit neuem Startbestand. Der alte bleibt als abgeschlossener Stand stehen — GoBD gilt auch in der Sandbox, gelöscht wird nichts.
  • Aus der Sandbox geht nichts nach draußen: keine E-Mail, keine ELSTER-Übermittlung, keine Bankverbindung, keine Belastung eines Zahlungsmittels.
  • Ein eigener Login in die Sandbox existiert nicht — erreichbar ist sie ausschließlich über die API, mit dem Sandbox-Schlüssel.
  • Rate-Limit 30 Anfragen pro Minute (Live-Schlüssel: 60 — beides am Schlüssel einstellbar).

Live-Schlüssel für deine echte Buchhaltung legst du an derselben Stelle an, sobald du mit der Sandbox durch bist.

Quickstart in drei Schritten

1. Sandbox-Schlüssel anlegen

Unter Mein Konto → API-Schlüssel einen Schlüssel mit Umgebung Sandbox und Scope write erzeugen. Er beginnt mit epago_test_ und wird nur einmal angezeigt — sofort sichern.

2. Verbindung testen

Die Antwort nennt Mandant, Schlüssel-Scopes und die Umgebung — an einem Sandbox-Schlüssel steht dort "apiKey.umgebung": "sandbox".

curl -H "Authorization: Bearer $EPAGO_KEY" \
  https://app.epago.de/api/v1/me
{
  "tenant": { "userId": "sandbox:…", "companyName": "Testmandant (Sandbox)" },
  "apiKey": { "keyId": "…", "permissions": ["read", "write"], "umgebung": "sandbox" }
}

3. Erste Buchung als Entwurf

Die vereinfachte Form: Bruttobetrag, Geldkonto, Sachkonto — der Server baut die Buchungszeilen samt Steuerzeile mit dem Kontenrahmen des Mandanten. Ein Entwurf (status: "draft", auch der Default) bucht nichts fest und lässt sich wieder löschen — der richtige Einstieg für die erste Anfrage. Die Kontonummern sind SKR03 (Quelle: wissen/datev-kontenrahmen/skr03-2026.json im Repository) — 1000 Kasse, 8400 Erlöse 19 % USt; im SKR04 sind es andere Nummern, die Antwort nennt den Kontenrahmen des Mandanten mit.

curl -X POST -H "Authorization: Bearer $EPAGO_KEY" \
  -H "Content-Type: application/json" \
  https://app.epago.de/api/v1/journal-entries \
  -d '{
    "date": "2026-06-12",
    "description": "Barverkauf",
    "status": "draft",
    "betrag": 119.00,
    "bruttoKonto": "1000",
    "sachKonto": "8400"
  }'

Weitere Beispiele — Buchung mit fertigen Zeilen, Storno, Zahlung an einem Beleg — stehen in der Referenz unten und in der Postman-Sammlung.

API-Referenz

Alle vierzehn Endpunkte mit Parametern, Beispielen und Antwortschemata — geladen zur Laufzeit aus derselben Spezifikation, aus der auch die Postman-Sammlung entsteht.

Authentifizierung und Grenzen

Schlüssel senden

Als Authorization: Bearer epago_… oder im x-api-key-Header. Ein Schlüssel gehört zu genau einem Mandanten; der Mandant kommt immer aus dem Schlüssel, nie aus dem Anfragekörper.

Scopes

read liest alles. write legt Buchungen an, storniert und lädt Belege hoch — schließt read ein. Nie Default: beim Erzeugen eines Schlüssels explizit wählen.

Rate-Limit

60 Anfragen/Minute je Live-Schlüssel, 30 je Sandbox-Schlüssel (Schiebefenster, einstellbar am Schlüssel), zusätzlich 600/Minute je IP. Das Limit gilt über alle Instanzen hinweg. Jede Antwort trägt den Zustand des Fensters in X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset; eine 429 zusätzlich in Retry-After (Sekunden).

Widerruf

Ein Schlüssel lässt sich jederzeit unter Mein Konto widerrufen oder ablaufen lassen — die Sperre wirkt sofort, ohne Cache-Verzögerung.

Fehlerformat und -codes

Jeder Fehler hat denselben Aufbau, ohne Stacktrace, ohne interne Pfade:

{ "error": { "code": "insufficient_scope", "message": "…" } }
HTTP Code Bedeutung
400 bad_request / validation_error Pflichtfeld fehlt, Soll ist nicht gleich Haben, ungueltige Steuerzeile oder Status.
400 file_too_large / unsupported_media_type Beleg-Upload: Datei ueber 10 MB oder nicht erlaubter Dateityp.
400 content_mismatch Beleg-Upload: Dateiinhalt passt nicht zum angegebenen mimeType (Magic-Bytes-Pruefung).
400 zahlungsart_erforderlich / begruendung_erforderlich / rueckzahlungsgrund_erforderlich Eine Zahlung laesst einen Rest offen oder ist eine Rueckzahlung: die Antwort traegt Vorschlag, Restbetrag und Optionen mit.
401 missing_api_key / invalid_api_key / api_key_expired / api_key_revoked Schluessel fehlt, ist ungueltig, abgelaufen oder widerrufen.
402 feature_locked / account_gesperrt Die API gehoert nicht zum Tarif des Mandanten, oder der Zugang ist wegen einer offenen Zahlung gesperrt.
403 insufficient_scope Der Schluessel hat den benoetigten Scope nicht (zum Beispiel read auf einer Schreib-Route).
404 not_found Ressource nicht gefunden — auch bei einer ID aus einem fremden Mandanten.
409 period_closed / conflict / cannot_reverse Festgeschriebene Periode, oder eine Buchung, die nicht stornierbar ist.
409 payment_not_allowed Auf diesen Beleg geht keine Zahlung ein (Entwurf, storniert, unbekannter Status).
423 konto_gesperrt_loeschantrag Der Mandant hat die Loeschung seiner Daten beantragt; bis zur Ruecknahme wird nicht mehr verarbeitet.
429 rate_limit_exceeded Rate-Limit ueberschritten. Retry-After nennt die Wartezeit in Sekunden.
500 internal_error / reverse_incomplete Serverfehler. Bei reverse_incomplete: die Gegenbuchung wurde angelegt, die Markierung des Originals ist fehlgeschlagen — nicht automatisch wiederholen, Support kontaktieren.

Versionierung und Änderungsverlauf

Die Version steht im Pfad (/api/v1). Innerhalb von v1 wird nur additiv geändert: neue Endpunkte, neue optionale Felder, neue Fehlerfelder. Ein bestehendes Feld verschwindet nicht und wechselt nicht die Bedeutung. info.version in der Spezifikation folgt SemVer; eine nicht additive Änderung bekäme /api/v2.

Version 1.2.0 2026-09-21
  • Jede Antwort traegt den Zustand des Rate-Limit-Fensters: X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset (Unix-Zeit in Sekunden).
  • Die Absage mit Status 429 nennt zusaetzlich Retry-After in Sekunden (RFC 9110, 10.2.3).
  • Das Limit gilt ueber alle Instanzen hinweg. Vorher zaehlte jede Function-Instanz fuer sich, das zugesagte Limit war damit nicht belastbar.
  • GET /me nennt die Umgebung des Schlüssels (umgebung: live oder sandbox). Sandbox-Schlüssel (epago_test_…) arbeiten auf dem Testmandanten des Accounts, den man unter Mein Konto → API-Schlüssel anlegt und zurücksetzt; sie erreichen die echte Buchhaltung nie und haben ein Limit von 30 Anfragen pro Minute.
Version 1.1.0 2026-09
  • Zahlungen an Belegen: POST /invoices/{id}/payments erfasst eine Zahlung, GET /invoices/{id}/payments liest die erfassten Zahlungen. Bleibt nach der Zahlung ein Rest offen, ist zahlungsart Pflicht (vollzahlung, teilzahlung, skonto, forderungsausfall); die Absage traegt Vorschlag, Restbetrag und Optionen mit, statt eine Entgeltminderung zu raten.
  • Vereinfachte Buchungsform: POST /journal-entries nimmt neben lines[] auch betrag, bruttoKonto und sachKonto. Die Buchungszeilen samt Steuerzeile baut der Server mit dem Kontenrahmen des Mandanten, und die Antwort sagt mit zeilen, steuerschluessel, split und kontenrahmen, was daraus geworden ist. Mit seite gibt der Aufrufer die Soll- oder Habenseite an, wo der Steuerfall sie nicht vorgibt.
  • Jede Operation nennt ihren Scope in x-scope (read oder write).
  • Die Absagen des Zugangs stehen jetzt an jeder Operation: 402 bei fehlendem Tarif oder gesperrtem Zugang (feature_locked, account_gesperrt), 423 bei einem laufenden Loeschantrag (konto_gesperrt_loeschantrag). Der 402 traegt seinen Fehlercode, statt als bad_request herauszukommen.
  • Die Beschreibung der Spezifikation nennt Grenzen, Fehlerformat und Versionsregel.
Version 1.0.0 2026-06
  • Start der Public API v1 mit 14 Endpunkten: Verbindungstest (/me), Kontenplan und Kontenblatt, Buchungen lesen, anlegen und stornieren, Rechnungen lesen, Beleg hochladen, BWA, Umsatzsteuer-Voranmeldung und Saldenliste.
  • Authentifizierung per API-Schluessel (Authorization: Bearer oder x-api-key) mit den Scopes read und write.
  • Schreibzugriffe laufen durch dieselbe Pruefschicht wie die Oberflaeche: Soll gleich Haben, Steuerautomatik, Periodensperre, Storno statt Loeschen.
  • Kein DELETE: gebuchte Saetze werden nicht geloescht, sondern storniert.

Postman

Die Sammlung entsteht aus derselben Spezifikation wie die Referenz oben — kein von Hand gepflegtes Duplikat.

  1. Beide Dateien in Postman importieren (Sammlung und Umgebung).
  2. Die Umgebung epago API oben rechts auswählen und in der Variable apiKey deinen Schlüssel eintragen — baseUrl steht bereits auf https://app.epago.de/api/v1.
  3. Anfrage auswählen und senden — für einen ersten Test eignet sich GET /me mit einem Sandbox-Schlüssel.

MCP-Server

epago-mcp ist ein zweiter Zugang zu derselben API — für Claude, Codex und andere MCP-fähige Werkzeuge. Er läuft lokal (stdio), braucht denselben API-Schlüssel wie ein direkter Aufruf und schreibt über dieselbe Prüfschicht. Zum Ausprobieren eignet sich ein Sandbox-Schlüssel.

Repository und Installationsanleitung auf GitHub

Was die API nicht kann

Damit hier nichts steht, was die API nicht hält: aktuell nicht möglich sind eine Rechnung anlegen, eine Mahnung auslösen, Kunden verwalten, ein PDF eines Belegs abrufen und jedes Löschen — gebuchte Sätze werden storniert, nicht gelöscht (GoBD). Lesend geht mehr als schreibend: Rechnungen, Zahlungen und Berichte lassen sich abrufen, angelegt werden sie noch nicht über die API. Was davon geplant ist, steht ohne Termin im Änderungsverlauf oben, sobald es so weit ist.