ehrlicheElster API

API-Dokumentation

Eine REST-API, um ehrlicheElster in andere Plattformen zu integrieren: Rechnungen, Kunden, Ausgaben, Projekte, Zeiterfassung und Angebote erstellen und abrufen, per Webhooks über Ereignisse informiert werden — mit API-Keys, Scopes, Idempotenz und Rate-Limits.

https://api.ehrlicheelster.de/v1API-Key erstellen

Einführung

Die ehrlicheElster API ist eine REST-Schnittstelle. Alle Anfragen laufen über HTTPS, Anfrage- und Antwortkörper sind JSON. Basis-URL:

https://api.ehrlicheelster.de/v1

Jede Anfrage gehört zu genau einer Organisation – bestimmt durch den verwendeten API-Key. Ressourcen, die du erstellst, erscheinen sofort in deinem ehrlicheElster-Konto und folgen denselben Steuer- und Nummernregeln wie manuell angelegte Belege.

Authentifizierung

Authentifiziere jede Anfrage mit einem API-Key aus Einstellungen → Sonstiges → API-Keys. Ein Key beginnt mit ee_live_ und wird nur einmal angezeigt. Sende ihn im Authorization-Header als Bearer-Token (alternativ x-api-key):

HTTP
Authorization: Bearer ee_live_dein_api_key

Keys tragen Scopes, die den Zugriff je Endpunkt begrenzen:

ScopeErlaubt
rechnungen:readRechnungen auflisten, abrufen, PDF laden
rechnungen:writeRechnungen erstellen, Status ändern
kunden:readKunden auflisten und abrufen
kunden:writeKunden anlegen und ändern
ausgaben:readAusgaben auflisten und abrufen
ausgaben:writeAusgaben anlegen und ändern
projekte:readProjekte auflisten und abrufen
projekte:writeProjekte anlegen und ändern
zeiten:readZeiteinträge und laufenden Timer abrufen
zeiten:writeZeiten erfassen, ändern, löschen, Timer steuern
angebote:readAngebote auflisten, abrufen, PDF laden
angebote:writeAngebote anlegen, ändern, in Rechnung umwandeln
webhooks:readWebhooks auflisten und abrufen
webhooks:writeWebhooks anlegen und löschen
Behandle API-Keys wie Passwörter. Ein kompromittierter Key kann jederzeit in den Einstellungen widerrufen werden – der Zugriff endet dann sofort.

Verbindung testen

GET/v1/me

Prüft den API-Key und liefert Organisation, Scopes und Key-Info zurück — ideal, um beim Einrichten einer Integration die Verbindung zu verifizieren. Jeder gültige Key funktioniert (kein spezieller Scope nötig).

cURL
curl https://api.ehrlicheelster.de/v1/me -H "Authorization: Bearer ee_live_dein_api_key"
JSON
{
  "object": "account",
  "organizationId": "665f…",
  "organizationName": "Muster GmbH",
  "scopes": ["rechnungen:read", "rechnungen:write", "kunden:read", "kunden:write", "ausgaben:read", "ausgaben:write", "webhooks:read", "webhooks:write"],
  "apiKey": { "id": "665f…", "name": "Zapier", "prefix": "ee_live_a1b2", "last4": "9f3d" }
}

Rate-Limits

Die API gibt es ab dem Pro-Plan. Pro API-Key sind im Pro-Plan 60 Anfragen pro Minute, im Business-Plan 120 Anfragen pro Minute erlaubt (Pro: 2 Keys und 5 Webhooks, Business: 20 Keys und 25 Webhooks). Jede Antwort enthält den aktuellen Stand; bei Überschreitung kommt 429 rate_limited mit einem Retry-After-Header.

HeaderBedeutung
X-RateLimit-LimitMaximale Anfragen pro Fenster.
X-RateLimit-RemainingVerbleibende Anfragen im aktuellen Fenster.
X-RateLimit-ResetUnix-Zeit (Sekunden), zu der das Fenster zurückgesetzt wird.

Idempotenz

Damit ein Netzwerk-Retry keine doppelte Rechnung erzeugt, sende bei POST-Anfragen einen Idempotency-Key-Header (z. B. eine UUID). Die erste Anfrage wird verarbeitet, jede Wiederholung mit demselben Key liefert exakt dieselbe Antwort zurück (Header Idempotent-Replayed: true). Keys gelten 24 Stunden.

HTTP
Idempotency-Key: 3f1c9a7e-8b2d-4e5f-9a1b-2c3d4e5f6a7b

Wird derselbe Key mit einem anderen Body verwendet, antwortet die API mit 422 idempotency_key_reused.

Pagination & Filter

List-Endpunkte sind cursor-basiert. Eine Antwort enthält data, has_more und next_cursor. Für die nächste Seite übergibst du next_cursor als starting_after.

Statuscodes & Fehler

Fehler liefern einen passenden HTTP-Status und einen JSON-Körper mit den Feldern error (Kürzel) und message (Klartext).

StatusCodeBedeutung
200 / 201OK / CreatedErfolg — Ressource geladen bzw. erstellt.
400validation_error · invalid_jsonEingabe fehlerhaft — Details im Feld message.
401unauthorizedAPI-Key fehlt, ist ungültig oder wurde widerrufen.
403forbiddenDem API-Key fehlt der nötige Scope für diesen Endpunkt.
404not_foundRessource existiert nicht (oder gehört nicht zu deiner Organisation).
409duplicate_rechnungsnummer · request_in_progress · invalid_status_transition · period_locked · entry_locked · timer_already_running · already_converted · duplicate_angebotsnummerKonflikt: Nummer bereits vergeben, idempotente Anfrage läuft noch, Statuswechsel nicht erlaubt, Periode festgeschrieben (GoBD), Zeiteintrag gesperrt, Timer läuft bereits, Angebot schon in Rechnung umgewandelt.
422idempotency_key_reusedIdempotency-Key mit abweichendem Body wiederverwendet.
429rate_limitedRate-Limit überschritten — siehe Retry-After-Header.
500internal_errorUnerwarteter Serverfehler.

Rechnung erstellen

POST/v1/rechnungen

Erstellt eine Rechnung. Netto-, Steuer- und Bruttobeträge werden serverseitig aus den Positionen berechnet, die Rechnungsnummer bei Bedarf automatisch vergeben. Scope: rechnungen:write.

Anfrageparameter

FeldTypBeschreibung
kundeobjectEmpfänger. Pflicht: name oder firmenname. Weitere Felder: strasse, hausnummer, plz, ort, land, email, telefon, ustIdNr, isGeschaeftskunde.
positionenarrayMindestens eine Position: beschreibung, menge, einzelpreis (netto) — plus optional einheit, steuersatz (0 | 7 | 19), rabatt (%).
rechnungsnummerstringOptional. Ohne Angabe wird die nächste Nummer nach deinem Format vergeben (z. B. RE-2026-001).
datumstringOptional, ISO-Datum. Standard: heute.
lieferdatumstringOptional, ISO-Datum (Leistungsdatum).
zahlungszielTagenumberOptional. Fälligkeitsdatum = datum + N Tage.
kleinunternehmerbooleanOptional. true setzt alle Positionen auf 0 % USt (§ 19 UStG).
waehrungstringOptional, ISO-4217 (Standard EUR). Bei Fremdwährung wird der EUR-Gegenwert eingefroren.
statusstringOptional: "entwurf" oder "offen" (Standard "offen").

Beispiel-Anfrage

JSON
{
  "kunde": {
    "firmenname": "Muster GmbH",
    "strasse": "Hauptstraße 12",
    "plz": "10115",
    "ort": "Berlin",
    "land": "DE",
    "email": "[email protected]",
    "ustIdNr": "DE123456789",
    "isGeschaeftskunde": true
  },
  "positionen": [
    { "beschreibung": "Webdesign – Konzept & Umsetzung", "menge": 12, "einheit": "Stunden", "einzelpreis": 95, "steuersatz": 19 },
    { "beschreibung": "Hosting (Jahrespauschale)", "menge": 1, "einheit": "Pauschale", "einzelpreis": 120, "steuersatz": 19 }
  ],
  "zahlungszielTage": 14
}
curl -X POST https://api.ehrlicheelster.de/v1/rechnungen \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "kunde": { "firmenname": "Muster GmbH", "plz": "10115", "ort": "Berlin" },
    "positionen": [
      { "beschreibung": "Beratung", "menge": 2, "einzelpreis": 150, "steuersatz": 19 }
    ],
    "zahlungszielTage": 14
  }'

Antwort (201 Created)

JSON
{
  "success": true,
  "id": "665f1a2b3c4d5e6f7a8b9c0d",
  "rechnungsnummer": "RE-2026-001",
  "status": "offen",
  "datum": "2026-06-29T00:00:00.000Z",
  "faelligkeitsdatum": "2026-07-13T00:00:00.000Z",
  "waehrung": "EUR",
  "nettobetrag": 1260,
  "umsatzsteuer": 239.4,
  "bruttobetrag": 1499.4,
  "steuerDetails": [ { "steuersatz": 19, "bemessungsgrundlage": 1260, "nettobetrag": 1260, "steuerbetrag": 239.4 } ]
}

Rechnungen auflisten

GET/v1/rechnungen

Liefert Rechnungen, neueste zuerst. Scope: rechnungen:read.

Query-Parameter

ParameterTypBeschreibung
limitnumber1–100, Standard 25.
starting_afterstringCursor: die id des letzten Elements der vorigen Seite (aus next_cursor).
statusstringFiltert nach Status (z. B. offen, bezahlt, storniert).
from / tostringISO-Datumsbereich über das Rechnungsdatum.
kundestringTeiltextsuche über den Kundennamen (case-insensitive).
cURL
curl "https://api.ehrlicheelster.de/v1/rechnungen?limit=25&status=offen" \
  -H "Authorization: Bearer ee_live_dein_api_key"

# Nächste Seite: next_cursor der vorigen Antwort als starting_after übergeben
curl "https://api.ehrlicheelster.de/v1/rechnungen?limit=25&starting_after=665f1a2b3c4d5e6f7a8b9c0d" \
  -H "Authorization: Bearer ee_live_dein_api_key"

Antwort

JSON
{
  "object": "list",
  "data": [
    { "id": "665f…", "rechnungsnummer": "RE-2026-042", "status": "offen", "bruttobetrag": 1499.4, "bezahlt": false, "datum": "2026-06-29T00:00:00.000Z", "kunde": "Muster GmbH" }
  ],
  "has_more": true,
  "next_cursor": "665f1a2b3c4d5e6f7a8b9c0d"
}

Rechnung abrufen

GET/v1/rechnungen/:id

Ruft eine einzelne Rechnung ab. Scope: rechnungen:read. Unbekannte IDs liefern 404 not_found.

cURL
curl https://api.ehrlicheelster.de/v1/rechnungen/665f1a2b3c4d5e6f7a8b9c0d \
  -H "Authorization: Bearer ee_live_dein_api_key"

Rechnung als PDF

GET/v1/rechnungen/:id/pdf

Liefert die Rechnung als PDF (application/pdf). Mit ?format=zugferd erhältst du ein hybrides ZUGFeRD/Factur-X-E-Rechnungs-PDF. Scope rechnungen:read.

cURL
curl https://api.ehrlicheelster.de/v1/rechnungen/665f…/pdf \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -o rechnung.pdf

Status ändern / bezahlt markieren

PATCH/v1/rechnungen/:id

Ändert den Status einer Rechnung (z. B. als bezahlt oder storniert markieren). GoBD-Regeln werden geprüft; das Buchungsjournal wird automatisch nachgezogen und passende Webhooks (rechnung.paid, rechnung.storniert) ausgelöst. Erlaubte Felder: status, bezahlt, bezahltAm. Scope rechnungen:write.

cURL
curl -X PATCH https://api.ehrlicheelster.de/v1/rechnungen/665f… \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "status": "bezahlt" }'

Ein unzulässiger Übergang (z. B. einer bereits stornierten Rechnung) antwortet mit 409 invalid_status_transition.

Kunden

Kunden anlegen, ändern und lesen — z. B. um deinen Kundenstamm mit einer anderen Plattform zu synchronisieren.

GET/v1/kunden
POST/v1/kunden
GET/v1/kunden/:id
PATCH/v1/kunden/:id

Pflicht beim Anlegen: name oder firmenname. Die Kundennummer wird automatisch vergeben (z. B. K0001). Listen unterstützen ?search= sowie Pagination. Scopes kunden:read / kunden:write.

cURL
curl -X POST https://api.ehrlicheelster.de/v1/kunden \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "firmenname": "Muster GmbH",
    "strasse": "Hauptstraße 12",
    "plz": "10115",
    "ort": "Berlin",
    "email": "[email protected]",
    "ustIdNr": "DE123456789",
    "umsatzsteuerKategorie": "inland"
  }'

Ausgaben

Ausgaben/Belege erfassen und lesen — z. B. um Kosten aus einer anderen Plattform zu importieren. Netto und Vorsteuer werden aus bruttobetrag und steuersatz berechnet; die Buchung ins Journal erfolgt automatisch.

GET/v1/ausgaben
POST/v1/ausgaben

Pflicht: lieferant, beschreibung, bruttobetrag, kategorie, datum. Identische Ausgaben (gleicher Betrag, Lieferant, Datum) werden automatisch als Dublette erkannt und zusammengeführt statt doppelt angelegt. Scopes ausgaben:read / ausgaben:write.

cURL
curl -X POST https://api.ehrlicheelster.de/v1/ausgaben \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "lieferant": "Amazon EU",
    "beschreibung": "Büromaterial",
    "bruttobetrag": 47.60,
    "steuersatz": 19,
    "kategorie": "buerobedarf",
    "datum": "2026-06-20"
  }'

Einzelne Ausgabe abrufen / ändern

GET/v1/ausgaben/:id
PATCH/v1/ausgaben/:id

PATCH aktualisiert nur die übergebenen Felder. Bei Änderung von bruttobetrag oder steuersatz werden Netto und Vorsteuer neu berechnet (Mischsätze via nettobetrag + vorsteuer überschreiben die Automatik). Ist der Beleg bereits ins Journal gebucht und die Periode festgeschrieben (GoBD), antwortet die API mit 409 period_locked — eine Änderung ist dann nur per Storno und Neuerfassung möglich. Scope ausgaben:write.

FeldTypBeschreibung
beschreibungstringFreitext-Beschreibung des Belegs.
datumstringISO-Datum des Belegs.
kategoriestringAusgabenkategorie (z. B. buerobedarf, bewirtung).
lieferantstringName des Lieferanten/Händlers.
bruttobetragnumberBruttobetrag. Bei Änderung werden nettobetrag und vorsteuer neu berechnet.
steuersatznumber0, 7 oder 19. Löst zusammen mit bruttobetrag eine Neuberechnung aus.
nettobetrag / vorsteuernumberOptionaler Mischsatz-Override: beide zusammen übergeben, dann werden sie exakt übernommen statt gerechnet.
waehrungstringISO-4217 (3 Buchstaben). Bei Fremdwährung wird der EUR-Gegenwert neu eingefroren.
bezahltbooleantrue setzt zusätzlich bezahltAm auf jetzt.
belegnummerstringInterne Belegnummer.
cURL
curl -X PATCH https://api.ehrlicheelster.de/v1/ausgaben/665f1a2b3c4d5e6f7a8b9c0d \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "bruttobetrag": 59.90, "steuersatz": 19, "bezahlt": true }'

Projekte

Projekte sind der Kostenträger für Zeiterfassung und Faktura. Anlegen, ändern und lesen — z. B. um Projekte aus einem PM-Tool zu spiegeln.

GET/v1/projekte
POST/v1/projekte
GET/v1/projekte/:id
PATCH/v1/projekte/:id

Die Projektnummer wird automatisch als P-1, P-2 … vergeben. Es gibt kein DELETE — archiviere stattdessen per PATCH mit status: "archiviert". Scopes projekte:read / projekte:write.

Query-Parameter (Liste)

ParameterTypBeschreibung
limitnumber1–100, Standard 25.
starting_afterstringCursor: die id des letzten Elements der vorigen Seite (aus next_cursor).
statusstringFiltert nach Status (aktiv, pausiert, abgeschlossen, archiviert).
kundeIdstringNur Projekte des angegebenen Kunden.
searchstringTeiltextsuche über Name und Projektnummer (case-insensitive).

Anfrageparameter (POST)

FeldTypBeschreibung
namestringPflicht. Name des Projekts (Kostenträger).
kundeIdstringOptional. Verknüpfung zu einem Kunden.
kundeNamestringOptional. Anzeigename des Kunden (Cache).
statusstringOptional: aktiv (Standard), pausiert, abgeschlossen, archiviert.
abrechnungsartstringOptional: "aufwand" (Standard) oder "nicht_abrechenbar".
stundensatznumberOptional. Standard-Stundensatz in EUR (Standard 0).
standardSteuersatznumberOptional: 0, 7 oder 19 (Standard 19).
abrechenbarDefaultbooleanOptional. Ob neue Zeiten standardmäßig abrechenbar sind (Standard true).
budgetStundennumberOptional. Stundenbudget des Projekts.
farbestringOptional. Farbmarkierung (z. B. #0071e3).
startDatum / endeDatumstringOptional. Laufzeit des Projekts.
notizstringOptional. Interne Notiz.
nummerstringOptional. Ohne Angabe wird automatisch P-1, P-2 … vergeben.

Beispiel

cURL
curl -X POST https://api.ehrlicheelster.de/v1/projekte \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Website-Relaunch Muster GmbH",
    "kundeName": "Muster GmbH",
    "stundensatz": 95,
    "standardSteuersatz": 19,
    "budgetStunden": 40
  }'

Antwort (201 Created)

JSON
{
  "success": true,
  "id": "665f1a2b3c4d5e6f7a8b9c0d",
  "nummer": "P-1",
  "name": "Website-Relaunch Muster GmbH",
  "status": "aktiv",
  "kundeId": null,
  "kundeName": "Muster GmbH",
  "abrechnungsart": "aufwand",
  "stundensatz": 95,
  "standardSteuersatz": 19,
  "abrechenbarDefault": true,
  "budgetStunden": 40,
  "taetigkeiten": [],
  "createdAt": "2026-07-06T10:00:00.000Z",
  "updatedAt": "2026-07-06T10:00:00.000Z"
}

Zeiterfassung

Zeiten auf Projekte buchen, ändern, löschen und per Timer live erfassen. Das datum ist immer ein Leistungsdatum im Format YYYY-MM-DD.

GET/v1/zeiten
POST/v1/zeiten
GET/v1/zeiten/:id
PATCH/v1/zeiten/:id
DELETE/v1/zeiten/:id
GET/v1/zeiten/timer
POST/v1/zeiten/timer

Einträge sind nur editier- und löschbar, solange sie status: "offen" haben und nicht festgeschrieben sind — fakturierte oder festgeschriebene Zeit ist schreibgeschützt und antwortet mit 409 entry_locked. Scopes zeiten:read / zeiten:write.

Query-Parameter (Liste)

ParameterTypBeschreibung
limitnumber1–100, Standard 25.
starting_afterstringCursor: die id des letzten Elements der vorigen Seite.
projektIdstringNur Zeiten des angegebenen Projekts.
statusstringFiltert nach Status (z. B. offen, fakturiert).
abrechenbarstring"true" oder "false" — filtert nach Abrechenbarkeit.
from / tostringDatumsbereich (YYYY-MM-DD) über das Leistungsdatum.

Anfrageparameter (POST)

FeldTypBeschreibung
projektIdstringPflicht. ID des Projekts, auf das die Zeit gebucht wird.
datumstringPflicht. Leistungsdatum im Format YYYY-MM-DD.
dauerMinutennumber | stringPflicht. Dauer in ganzen Minuten — oder Freitext wie "1:30", "1,5h", "90m".
beschreibungstringOptional. Tätigkeitsbeschreibung (Standard leer).
abrechenbarbooleanOptional. Standard = abrechenbarDefault des Projekts.
stundensatznumberOptional. Überschreibt den Satz; wird bei Erfassung eingefroren.
taetigkeitId / taetigkeitstringOptional. Tätigkeit per ID (aus dem Projekt) oder als Freitext.

Beispiel

cURL
curl -X POST https://api.ehrlicheelster.de/v1/zeiten \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "projektId": "665f1a2b3c4d5e6f7a8b9c0d",
    "datum": "2026-07-06",
    "dauerMinuten": "1:30",
    "beschreibung": "Frontend-Arbeiten"
  }'

Antwort (201 Created)

JSON
{
  "success": true,
  "id": "665f…",
  "projektId": "665f1a2b3c4d5e6f7a8b9c0d",
  "projektName": "Website-Relaunch Muster GmbH",
  "datum": "2026-07-06",
  "dauerMinuten": 90,
  "beschreibung": "Frontend-Arbeiten",
  "abrechenbar": true,
  "stundensatz": 95,
  "status": "offen",
  "laufend": false,
  "festgeschrieben": false,
  "quelle": "manuell",
  "createdAt": "2026-07-06T12:30:00.000Z",
  "updatedAt": "2026-07-06T12:30:00.000Z"
}

Timer (start / stop / cancel)

Ein laufender Timer ist ein Zeiteintrag mit laufend: true — es gibt genau einen je API-Key. stop finalisiert die Dauer, lässt den Eintrag aber auf status: "offen" (Mensch-Verify vor der Faktura); cancel verwirft ihn. Ein zweiter start bei laufendem Timer antwortet mit 409 timer_already_running.

cURL
# Timer starten
curl -X POST https://api.ehrlicheelster.de/v1/zeiten/timer \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "action": "start", "projektId": "665f1a2b3c4d5e6f7a8b9c0d", "beschreibung": "Bugfixing" }'

# Laufenden Timer abfragen  →  { "timer": { … } | null }
curl https://api.ehrlicheelster.de/v1/zeiten/timer -H "Authorization: Bearer ee_live_dein_api_key"

# Timer stoppen (Eintrag bleibt status:"offen") oder verwerfen
curl -X POST https://api.ehrlicheelster.de/v1/zeiten/timer \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "action": "stop" }'   # alternativ: { "action": "cancel" }

Angebote

Angebote erstellen, ändern, als PDF laden und in eine Rechnung umwandeln. Aufbau und Steuerlogik entsprechen den Rechnungen — Netto-, Steuer- und Bruttobeträge werden serverseitig aus den Positionen berechnet.

GET/v1/angebote
POST/v1/angebote
GET/v1/angebote/:id
PATCH/v1/angebote/:id
POST/v1/angebote/:id/convert
GET/v1/angebote/:id/pdf

Status-Ablauf: entwurf → versendet → angenommen / abgelehnt → in_rechnung. Der Wechsel auf angenommen löst das Webhook-Event angebot.angenommen aus. Scopes angebote:read / angebote:write.

Query-Parameter (Liste)

ParameterTypBeschreibung
limitnumber1–100, Standard 25.
starting_afterstringCursor: die id des letzten Elements der vorigen Seite.
statusstringFiltert nach Status (entwurf, versendet, angenommen, abgelehnt, in_rechnung).
from / tostringISO-Datumsbereich über das Angebotsdatum.
kundestringTeiltextsuche über den Empfängernamen (case-insensitive).

Anfrageparameter (POST)

FeldTypBeschreibung
kundeobjectEmpfänger. Pflicht: name oder firmenname. Weitere Felder: ansprechpartner, strasse, hausnummer, plz, ort, land, email, telefon, ustIdNr, kundenNr, isGeschaeftskunde.
positionenarrayMindestens eine Position: beschreibung, menge, einzelpreis (netto) — plus optional einheit, steuersatz (0–100), rabatt (%).
angebotsnummerstringOptional. Ohne Angabe wird die nächste Nummer vergeben (z. B. AG-2026-001).
datumstringOptional, ISO-Datum. Standard: heute.
gueltigkeitsdauernumberOptional. Gültigkeit in Tagen (Standard 30). Setzt gueltigBis = datum + N.
zahlungszielnumberOptional. Zahlungsziel in Tagen (Standard 14).
kleinunternehmerbooleanOptional. true setzt alle Positionen auf 0 % USt (§ 19 UStG).
waehrungstringOptional, ISO-4217 (Standard EUR).
notizenstringOptional. Freitext auf dem Angebot.
statusstringOptional: "entwurf" (Standard) oder "versendet".

Angebot erstellen

cURL
curl -X POST https://api.ehrlicheelster.de/v1/angebote \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "kunde": { "firmenname": "Muster GmbH", "plz": "10115", "ort": "Berlin" },
    "positionen": [
      { "beschreibung": "Konzept & Design", "menge": 8, "einzelpreis": 110, "steuersatz": 19 }
    ],
    "gueltigkeitsdauer": 30,
    "zahlungsziel": 14
  }'

In Rechnung umwandeln

Erzeugt aus dem Angebot eine Rechnung (Status entwurf), bucht sie ins Journal und markiert das Angebot als in_rechnung. Ein bereits umgewandeltes Angebot antwortet bei erneutem Aufruf mit 409 already_converted. Das PDF liefert GET /v1/angebote/:id/pdf als application/pdf.

cURL
curl -X POST https://api.ehrlicheelster.de/v1/angebote/665f…/convert \
  -H "Authorization: Bearer ee_live_dein_api_key"

# Antwort: die erzeugte Rechnung (status "entwurf") — 201 Created.
# Erneuter Aufruf → 409 already_converted (mit rechnungId & rechnungsnummer).

Webhooks

Registriere eine HTTPS-URL, um bei Ereignissen benachrichtigt zu werden — ideal, um nachgelagerte Prozesse in anderen Plattformen auszulösen (z. B. Fulfillment bei Zahlung).

Verfügbare Events

EventAusgelöst wenn
rechnung.createdEine Rechnung erstellt wurde (App oder API).
rechnung.paidEine Rechnung als bezahlt markiert wurde.
rechnung.storniertEine Rechnung storniert wurde.
kunde.createdEin Kunde angelegt wurde.
ausgabe.createdEine Ausgabe erfasst wurde.
projekt.createdEin Projekt angelegt wurde.
zeiteintrag.createdEin Zeiteintrag erfasst wurde.
angebot.createdEin Angebot erstellt wurde.
angebot.angenommenEin Angebot als angenommen markiert wurde.

Webhook registrieren

POST/v1/webhooks

Scope webhooks:write. Die Antwort enthält einmalig das secret zum Prüfen der Signaturen. Endpunkte verwalten: GET /v1/webhooks, DELETE /v1/webhooks/:id.

cURL
curl -X POST https://api.ehrlicheelster.de/v1/webhooks \
  -H "Authorization: Bearer ee_live_dein_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://deine-app.de/hooks/ehrlicheelster",
    "events": ["rechnung.created", "rechnung.paid"]
  }'

# Antwort enthält EINMALIG das Signatur-Secret:
# { "id": "…", "url": "…", "events": [...], "secret": "whsec_…" }

Zustellung & Payload

Wir senden ein POST mit dem Event im Header X-EhrlicheElster-Event und einer Signatur in X-EhrlicheElster-Signature. Fehlgeschlagene Zustellungen werden bis zu dreimal wiederholt.

HTTP
POST https://deine-app.de/hooks/ehrlicheelster
X-EhrlicheElster-Event: rechnung.paid
X-EhrlicheElster-Signature: t=1751200000,v1=9f2b…

{
  "id": "evt_2a1b3c…",
  "type": "rechnung.paid",
  "created": 1751200000,
  "data": {
    "id": "665f…",
    "rechnungsnummer": "RE-2026-001",
    "status": "bezahlt",
    "bezahlt": true,
    "bruttobetrag": 1499.4
  }
}

Signatur prüfen

Der Header hat das Format t=<timestamp>,v1=<hmac>. Berechne HMAC-SHA256 über `${t}.${rawBody}` mit deinem Webhook-Secret und vergleiche konstant-zeitig.

JavaScript
import crypto from "crypto";

// Wichtig: den ROHEN Request-Body verwenden (nicht den geparsten!).
function istGueltig(rawBody, header, secret) {
  const teile = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const erwartet = crypto
    .createHmac("sha256", secret)
    .update(`${teile.t}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(teile.v1), Buffer.from(erwartet));
}

SDKs & Integrationen

Aktuell gibt es kein offizielles SDK — die API ist bewusst eine schlanke REST-Schnittstelle, die sich mit jeder Standard-HTTP-Bibliothek ansprechen lässt. Die Beispiele in dieser Doku zeigen genau das: cURL, fetch in JavaScript und requests in Python. Alles, was du brauchst, ist der Bearer-Token im Authorization-Header.

Für No-Code-Anbindungen lassen sich Webhooks + REST mit Automatisierungs-Tools wie Zapier oder Make kombinieren: ein Webhook (z. B. rechnung.paid) stößt einen Ablauf an, der per REST weitere Ressourcen liest oder anlegt.

MCP-Server für KI-Assistenten

ehrlicheElster spricht das Model Context Protocol (MCP). Damit arbeiten Claude, ChatGPT, Cursor und andere MCP-fähige Assistenten direkt mit deiner Buchhaltung — Rechnungsentwürfe anlegen, Belege ansehen und Bankzahlungen zuordnen, Kunden und Produkte nachschlagen — ohne die App zu öffnen. Du meldest dich mit deinem ehrlicheElster-Konto an (OAuth 2.1), siehst auf der Freigabeseite genau, welche Rechte der Assistent bekommt, und kannst die Verbindung jederzeit trennen. Derzeit im Betatest: die Freigabe verlangt einen Freischaltcode.

https://api.ehrlicheelster.de/v1/mcp

Verbinden

  • claude.ai / Claude Desktop: Customize → Connectors → „Add custom connector“ → Adresse einfügen → „Sign in“ → anmelden → Rechte freigeben.
  • Claude Code: claude mcp add --transport http ehrlicheelster https://api.ehrlicheelster.de/v1/mcp, danach /mcp → anmelden.
  • Cursor / ChatGPT / andere: als Remote-MCP-Server mit OAuth hinzufügen. Alternativ akzeptiert der Server einen API-Key als Authorization: Bearer ee_live_… (nur mit den Scopes des Keys).

Was der Assistent darf

Rechte werden je Verbindung als Scopes vergeben — dieselben Namen wie bei API-Keys: rechnungen:read/write, kunden:read/write, ausgaben:read/write, bank:read/write, dazu berichte, steuern, buchhaltung, lohn, projekte, zeiten, vertraege, kontakte, aufgaben. Werkzeuge, die dein Tarif nicht enthält (z. B. Bankumsätze ohne Bankanbindung), sieht der Assistent gar nicht erst.

  • Verkaufen: Kunden und Produkte, Angebote, Auftragsbestätigungen, Lieferscheine, Rechnungen (Entwurf, Gutschrift, Storno, Abschlag/Schluss), Mahnungen, Versand per E-Mail, Zahlungseingänge.
  • Belege & Ausgaben: wartende Belege finden, Beleg als Bild ansehen, Ausgaben und Einnahmen, Eingangsrechnungen (auch aus PDF/XRechnung), Kontierung und Regeln, Belegdateien hochladen — direkt aus dem Assistenten oder über einen gebundenen Upload-Link.
  • Bank: Kontostände, Umsätze, Zuordnung zu Belegen und Rechnungen, Reste schließen.
  • Berichte & Steuern: GuV, BWA, EÜR, Cashflow, offene Posten, Steuerübersicht und -rücklage, UStVA-Posten, Zusammenfassende Meldung, Steuerwissen mit Fundstelle.
  • Team, Projekte, Verträge, Aufgaben: Mitarbeiter und Lohnabrechnungen (eigener Scope, Beschäftigtendaten), Projekte und Zeiterfassung, Verträge, Kontakte, Aufgaben.

Insgesamt 118 Werkzeuge, dazu Einstiegs-Prompts (Rechnung schreiben, Belege zuordnen, offene Posten, Monatsüberblick) und die Firmeneinstellungen als Resource.

Grundsätze: Rechnungen entstehen als Entwurf, Zuordnungen brauchen deine Bestätigung im Chat, nichts wird still gebucht oder ans Finanzamt gesendet. Jeder Zugriff steht mit deinem Namen im Audit-Log deiner Firma.

Datenschutz

Der verbundene Assistent ruft die freigegebenen Daten bei seinemAnbieter ab — je nach Anbieter und Tarif auch außerhalb der EU. Er ist dein Auftragsverarbeiter, nicht unserer; unser Server steht in Deutschland. Wir empfehlen einen Business-Tarif mit Auftragsverarbeitungsvertrag (z. B. Claude Team/Enterprise oder API-Zugang), das Trainings-Opt-out beim Anbieter zu prüfen und nur die Rechte freizugeben, die du brauchst. Details in der Datenschutzerklärung und im AVV.

Support

Fragen zur API oder ein Problem mit einer Integration? Schreib uns an [email protected] — oder verwalte deine API-Keys direkt in den Einstellungen. Ein kompromittierter Key kann dort jederzeit widerrufen werden.