Flowdue API v1
Die Flowdue-API ermöglicht es Ihnen, Rechnungen, Kunden und Webhooks programmatisch zu verwalten.
Authentifizierung
Jede Anfrage muss einen API-Key über den Authorization-Header als
Bearer-Token senden. API-Keys werden nur einmalig bei der Erstellung angezeigt
und sollten sicher gespeichert werden.
Authorization: Bearer ak_live_xxxxxxxxxxxxxxxxxxxxxxxx
Basis-URL
https://flowdue.de/api/v1
Alle Endpunkte liefern ausschließlich JSON. Alle Datumsangaben folgen dem Format YYYY-MM-DD.
Rate-Limits
| Limit | Wert |
|---|---|
| Anfragen pro Key | 1.000 / 24 Stunden |
| Seitengröße (page size) | Max. 100 Einträge pro Anfrage |
| Webhooks | Max. 5 Webhooks pro Konto (aktiv und inaktiv) |
| API-Keys | Max. 5 aktive Keys |
Bei Überschreitung antwortet die API mit HTTP 429 und dem Code
RATE_LIMIT_EXCEEDED.
Fehler
Fehler werden einheitlich im folgenden Format zurückgegeben:
{
"success": false,
"code": "NOT_FOUND",
"message": "Rechnung nicht gefunden."
}
| HTTP | Code | Bedeutung |
|---|---|---|
| 400 | VALIDATION_ERROR / CURRENCY_NOT_SUPPORTED / INVOICE_TAX_MISMATCH | Ungültige Parameter, falsche Währung oder falscher Steuerbetrag |
| 401 | INVALID_API_KEY / KEY_EXPIRED / KEY_REVOKED / MISSING_API_KEY / INVALID_KEY_FORMAT | Authentifizierungsfehler |
| 403 | IP_BLOCKED / ACCOUNT_INACTIVE / FEATURE_UNAVAILABLE | Zugriff gesperrt |
| 404 | NOT_FOUND | Ressource nicht gefunden |
| 409 | DUPLICATE_INVOICE_NUMBER / DUPLICATE_EMAIL | Eindeutigkeitsverletzung |
| 422 | WEBHOOK_LIMIT | Maximale Anzahl erreicht |
| 429 | RATE_LIMIT_EXCEEDED | Rate-Limit überschritten |
| 500 | INTERNAL_ERROR / AUTH_ERROR / ENCRYPTION_ERROR | Interner Serverfehler bzw. fehlender Verschlüsselungsschlüssel |
Rechnungen
Scope: invoices:read / invoices:write
/api/v1/invoices
Listet alle Rechnungen mit Pagination und Filtern.
Query-Parameter
| Parameter | Typ | Optional | Beschreibung |
|---|---|---|---|
| page | integer | ja | Seitennummer, beginnt bei 1 (Standard: 1) |
| limit | integer | ja | Einträge pro Seite, max. 100 (Standard: 50) |
| status | string | ja | Statusfilter: pending, overdue, paid, draft, cancelled, refunded |
| from | date | ja | Rechnungsdatum ab (YYYY-MM-DD) |
| to | date | ja | Rechnungsdatum bis (YYYY-MM-DD) |
Antwort (200)
{
"success": true,
"data": [
{
"id": 42,
"invoice_number": "RE-2026-001",
"invoice_date": "2026-07-01",
"due_date": "2026-07-31",
"status": "pending",
"subtotal": "1000.00",
"tax_rate": "19",
"tax_amount": "190.00",
"total": "1190.00",
"currency": "EUR",
"notes": null,
"client_name": "Max Mustermann",
"client_company": "Muster GmbH"
}
],
"meta": {
"total": 1,
"page": 1,
"limit": 50
}
}
/api/v1/invoices/:id
Liefert eine einzelne Rechnung inklusive aller Felder (ohne Rechnungspositionen).
Antwort (200)
{
"success": true,
"data": {
"id": 42,
"user_id": 7,
"client_id": 12,
"invoice_number": "RE-2026-001",
"invoice_date": "2026-07-01",
"due_date": "2026-07-31",
"status": "pending",
"subtotal": "1000.00",
"tax_rate": "19",
"tax_amount": "190.00",
"total": "1190.00",
"currency": "EUR",
"notes": null,
"client_name": "Max Mustermann",
"created_at": "2026-07-01T09:00:00.000Z"
}
}
Bei unbekannter ID antwortet die API mit 404 NOT_FOUND.
/api/v1/invoices
Erstellt eine neue Rechnung. Der Kunde muss bereits existieren.
Body (JSON)
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| invoice_number | string | ja | Eindeutige Rechnungsnummer (max. 50 Zeichen) |
| client_id | integer | ja | ID des Kunden |
| invoice_date | date | ja | Rechnungsdatum (YYYY-MM-DD) |
| due_date | date | ja | Fälligkeitsdatum (YYYY-MM-DD) |
| subtotal | number | ja | Netto-Betrag (0.01 – 1.000.000) |
| tax_rate | number | nein | Steuersatz in Prozent (Standard: 19) |
| currency | string | nein | Währung – es wird nur EUR unterstützt, andere Werte werden mit 400 CURRENCY_NOT_SUPPORTED abgelehnt |
| notes | string | nein | Interne Notizen |
Beispiel-Request
curl -X POST "https://flowdue.de/api/v1/invoices" \
-H "Authorization: Bearer ak_live_..." \
-H "Content-Type: application/json" \
-d '{
"invoice_number": "RE-2026-001",
"client_id": 12,
"invoice_date": "2026-07-01",
"due_date": "2026-07-31",
"subtotal": 1000.00,
"tax_rate": 19
}'
Antwort (201)
{
"success": true,
"data": {
"id": 42,
"invoice_number": "RE-2026-001",
"status": "pending",
"total": "1190.00",
...
}
}
Löst das Webhook-Event invoice.created aus.
/api/v1/invoices/:id/mark-paid
Markiert eine Rechnung als bezahlt und setzt das Zahlungsdatum auf das aktuelle Datum.
Antwort (200)
{
"success": true,
"data": {
"id": 42,
"status": "paid",
"paid_date": "2026-08-01"
}
}
Löst das Webhook-Event invoice.paid aus.
/api/v1/invoices/:id/mark-unpaid
Setzt eine bezahlte Rechnung zurück auf ausstehend bzw. überfällig (anhand Fälligkeitsdatum).
Antwort (200)
{
"success": true,
"data": {
"id": 42,
"status": "pending"
}
}
Löst das Webhook-Event invoice.updated aus.
Kunden
Scope: clients:read / clients:write
/api/v1/clients
Listet alle Kunden mit Pagination.
Query-Parameter
| Parameter | Typ | Optional | Beschreibung |
|---|---|---|---|
| page | integer | ja | Seitennummer (Standard: 1) |
| limit | integer | ja | Einträge pro Seite, max. 100 |
| search | string | ja | Suchbegriff für Name, Firma oder E-Mail |
Antwort (200)
{
"success": true,
"data": [
{
"id": 12,
"first_name": "Max",
"last_name": "Mustermann",
"company": "Muster GmbH",
"email": "max@mustermann.de",
"mobile": null,
"status": "active"
}
],
"meta": {
"total": 1,
"page": 1,
"limit": 50
}
}
/api/v1/clients
Erstellt einen neuen Kunden.
Body (JSON)
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| first_name | string | ja | Vorname (max. 100 Zeichen) |
| last_name | string | ja | Nachname (max. 100 Zeichen) |
| string | ja | E-Mail-Adresse – pro Konto eindeutig, Groß-/Kleinschreibung wird normalisiert | |
| company | string | nein | Firmenname (max. 255 Zeichen) |
| mobile | string | nein | Mobilnummer – max. 30 Zeichen, nur Ziffern, Leerzeichen, +, - und Klammern |
| address | string | nein | Straße und Hausnummer (max. 500 Zeichen) |
| city | string | nein | Stadt (max. 100 Zeichen) |
| zip_code | string | nein | Postleitzahl (max. 20 Zeichen) |
| country | string | nein | Land (max. 100 Zeichen) |
| tax_number | string | nein | Steuernummer (max. 50 Zeichen) |
| vat_number | string | nein | USt-IdNr. (max. 50 Zeichen) |
| iban | string | nein | IBAN – zwei Buchstaben, zwei Ziffern und bis zu 30 alphanumerische Zeichen; Leerzeichen werden ignoriert |
| bic | string | nein | BIC/SWIFT-Code (max. 11 Zeichen) |
| status | string | nein | active oder inactive (Standard: active) |
| notes | string | nein | Interne Notizen (max. 2000 Zeichen) |
Sensible Felder – address, city, zip_code, country, tax_number, vat_number, iban, bic und notes – werden AES-256-GCM-verschlüsselt gespeichert, wie im Flowdue-Frontend.
Antwort (201)
{
"success": true,
"data": {
"id": 12,
"first_name": "Max",
"last_name": "Mustermann",
"email": "max@mustermann.de",
"company": "Muster GmbH",
"mobile": null,
"status": "active"
}
}
Löst das Webhook-Event client.created aus. Bei doppelter E-Mail antwortet die API mit 409 DUPLICATE_EMAIL (die Prüfung ist unabhängig von Groß- und Kleinschreibung).
Webhook-Endpunkte
Scope: webhooks
/api/v1/webhooks
Listet alle Webhooks des Kontos.
Antwort (200)
{
"success": true,
"data": [
{
"id": 3,
"url": "https://example.com/flowdue-hook",
"events": ["invoice.paid", "invoice.created"],
"is_active": true,
"last_triggered": null,
"failure_count": 0
}
]
}
/api/v1/webhooks
Registriert einen neuen Webhook. Die URL muss per HTTPS erreichbar und darf keine private IP-Adresse sein.
Body (JSON)
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| url | string | ja | HTTPS-URL, die benachrichtigt wird |
| events | string[] | ja | Mind. 1 Event (siehe unten) |
Antwort (201)
{
"success": true,
"data": {
"id": 3,
"url": "https://example.com/flowdue-hook",
"events": ["invoice.paid"],
"is_active": true,
"secret": "a1b2c3..."
},
"message": "Secret sicher speichern – wird nicht erneut angezeigt"
}
/api/v1/webhooks/:id
Löscht einen Webhook.
Antwort (200)
{
"success": true,
"message": "Webhook gelöscht"
}
Webhook-Events
Flowdue sendet bei folgenden Ereignissen eine Benachrichtigung an Ihre registrierten Webhooks:
| Event | Beschreibung |
|---|---|
invoice.created | Eine Rechnung wurde erstellt |
invoice.updated | Eine Rechnung wurde aktualisiert |
invoice.paid | Eine Rechnung wurde als bezahlt markiert |
reminder.sent | Eine Mahnung wurde versendet |
client.created | Ein Kunde wurde erstellt |
Fehlgeschlagene Zustellungen werden bis zu 2 Mal mit exponentiellem Backoff (10s, 20s) wiederholt (insgesamt maximal 3 Versuche). Nach 10 aufeinanderfolgenden Fehlern wird der Webhook automatisch deaktiviert.
Signatur-Prüfung
Jede Webhook-Zustellung enthält zwei Header, mit denen Sie die Authentizität prüfen können:
X-Flowdue-Event: invoice.paid
X-Flowdue-Signature: sha256=<hmac>
Die Signatur ist ein HMAC-SHA256 über den rohen Request-Body unter Verwendung des
secret aus der Webhook-Erstellung:
signature = HMAC_SHA256(secret, body)
Der Body hat das folgende Format:
{
"event": "invoice.paid",
"data": { "id": 42, "status": "paid" },
"timestamp": "2026-08-01T12:00:00.000Z"
}
Beispiel: Node.js
import crypto from "crypto";
function verifyWebhook(body, signature, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
timestamp älter als 5 Minuten ist,
um Replay-Angriffe zu verhindern.
Scopes
API-Keys werden mit Rechten ausgestattet. Verwenden Sie nur die Scopes, die Ihre Integration benötigt.
| Scope | Berechtigung |
|---|---|
invoices:read | Rechnungen lesen |
invoices:write | Rechnungen erstellen und aktualisieren |
clients:read | Kunden lesen |
clients:write | Kunden erstellen |
webhooks | Webhooks verwalten |
Anfragen ohne den erforderlichen Scope werden wie nicht existierende Endpunkte behandelt
und antworten mit 404 NOT_FOUND.
Code-Beispiele
cURL – Rechnungen auflisten
curl "https://flowdue.de/api/v1/invoices?status=overdue&page=1&limit=50" \
-H "Authorization: Bearer ak_live_..."
JavaScript (fetch)
const res = await fetch("https://flowdue.de/api/v1/clients", {
headers: { Authorization: "Bearer " + apiKey },
});
const { data } = await res.json();
console.log(data);
Python (requests)
import requests
resp = requests.post(
"https://flowdue.de/api/v1/invoices",
headers={"Authorization": f"Bearer {api_key}"},
json={
"invoice_number": "RE-2026-001",
"client_id": 12,
"invoice_date": "2026-07-01",
"due_date": "2026-07-31",
"subtotal": 1000.00,
},
)
print(resp.status_code, resp.json())