Flowdue API v1

Die Flowdue-API ermöglicht es Ihnen, Rechnungen, Kunden und Webhooks programmatisch zu verwalten.

API-Zugriff erfordert einen persönlichen API-Key. Erstellen Sie Ihren Key unter Einstellungen → Entwickler-API.

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
Keys werden als SHA-256-Hash in der Datenbank gespeichert und sind weder über die API noch über die Oberfläche erneut abrufbar. Bei Verlust müssen Sie einen neuen Key erstellen.

Basis-URL

https://flowdue.de/api/v1

Alle Endpunkte liefern ausschließlich JSON. Alle Datumsangaben folgen dem Format YYYY-MM-DD.

Rate-Limits

LimitWert
Anfragen pro Key1.000 / 24 Stunden
Seitengröße (page size)Max. 100 Einträge pro Anfrage
WebhooksMax. 5 Webhooks pro Konto (aktiv und inaktiv)
API-KeysMax. 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."
}
HTTPCodeBedeutung
400VALIDATION_ERROR / CURRENCY_NOT_SUPPORTED / INVOICE_TAX_MISMATCHUngültige Parameter, falsche Währung oder falscher Steuerbetrag
401INVALID_API_KEY / KEY_EXPIRED / KEY_REVOKED / MISSING_API_KEY / INVALID_KEY_FORMATAuthentifizierungsfehler
403IP_BLOCKED / ACCOUNT_INACTIVE / FEATURE_UNAVAILABLEZugriff gesperrt
404NOT_FOUNDRessource nicht gefunden
409DUPLICATE_INVOICE_NUMBER / DUPLICATE_EMAILEindeutigkeitsverletzung
422WEBHOOK_LIMITMaximale Anzahl erreicht
429RATE_LIMIT_EXCEEDEDRate-Limit überschritten
500INTERNAL_ERROR / AUTH_ERROR / ENCRYPTION_ERRORInterner Serverfehler bzw. fehlender Verschlüsselungsschlüssel

Rechnungen

Scope: invoices:read / invoices:write

GET /api/v1/invoices

Listet alle Rechnungen mit Pagination und Filtern.

Query-Parameter

ParameterTypOptionalBeschreibung
pageintegerjaSeitennummer, beginnt bei 1 (Standard: 1)
limitintegerjaEinträge pro Seite, max. 100 (Standard: 50)
statusstringjaStatusfilter: pending, overdue, paid, draft, cancelled, refunded
fromdatejaRechnungsdatum ab (YYYY-MM-DD)
todatejaRechnungsdatum 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
  }
}
GET /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.

POST /api/v1/invoices

Erstellt eine neue Rechnung. Der Kunde muss bereits existieren.

Body (JSON)

FeldTypPflichtBeschreibung
invoice_numberstringjaEindeutige Rechnungsnummer (max. 50 Zeichen)
client_idintegerjaID des Kunden
invoice_datedatejaRechnungsdatum (YYYY-MM-DD)
due_datedatejaFälligkeitsdatum (YYYY-MM-DD)
subtotalnumberjaNetto-Betrag (0.01 – 1.000.000)
tax_ratenumberneinSteuersatz in Prozent (Standard: 19)
currencystringneinWährung – es wird nur EUR unterstützt, andere Werte werden mit 400 CURRENCY_NOT_SUPPORTED abgelehnt
notesstringneinInterne 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.

PUT /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.

PUT /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

GET /api/v1/clients

Listet alle Kunden mit Pagination.

Query-Parameter

ParameterTypOptionalBeschreibung
pageintegerjaSeitennummer (Standard: 1)
limitintegerjaEinträge pro Seite, max. 100
searchstringjaSuchbegriff 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
  }
}
POST /api/v1/clients

Erstellt einen neuen Kunden.

Body (JSON)

FeldTypPflichtBeschreibung
first_namestringjaVorname (max. 100 Zeichen)
last_namestringjaNachname (max. 100 Zeichen)
emailstringjaE-Mail-Adresse – pro Konto eindeutig, Groß-/Kleinschreibung wird normalisiert
companystringneinFirmenname (max. 255 Zeichen)
mobilestringneinMobilnummer – max. 30 Zeichen, nur Ziffern, Leerzeichen, +, - und Klammern
addressstringneinStraße und Hausnummer (max. 500 Zeichen)
citystringneinStadt (max. 100 Zeichen)
zip_codestringneinPostleitzahl (max. 20 Zeichen)
countrystringneinLand (max. 100 Zeichen)
tax_numberstringneinSteuernummer (max. 50 Zeichen)
vat_numberstringneinUSt-IdNr. (max. 50 Zeichen)
ibanstringneinIBAN – zwei Buchstaben, zwei Ziffern und bis zu 30 alphanumerische Zeichen; Leerzeichen werden ignoriert
bicstringneinBIC/SWIFT-Code (max. 11 Zeichen)
statusstringneinactive oder inactive (Standard: active)
notesstringneinInterne 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

GET /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
    }
  ]
}
POST /api/v1/webhooks

Registriert einen neuen Webhook. Die URL muss per HTTPS erreichbar und darf keine private IP-Adresse sein.

Body (JSON)

FeldTypPflichtBeschreibung
urlstringjaHTTPS-URL, die benachrichtigt wird
eventsstring[]jaMind. 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"
}
DELETE /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:

EventBeschreibung
invoice.createdEine Rechnung wurde erstellt
invoice.updatedEine Rechnung wurde aktualisiert
invoice.paidEine Rechnung wurde als bezahlt markiert
reminder.sentEine Mahnung wurde versendet
client.createdEin 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)
  );
}
Verwerfen Sie Zustellungen, deren 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.

ScopeBerechtigung
invoices:readRechnungen lesen
invoices:writeRechnungen erstellen und aktualisieren
clients:readKunden lesen
clients:writeKunden erstellen
webhooksWebhooks 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())