Fluss
API-Dokumentation
← Zum Board

Fluss REST-API

Alle Board-, Spalten- und Karten-Funktionen sind über eine JSON-REST-API erreichbar — per Session-Cookie (UI) oder per API-Token (externe Clients).

Basis

Authentifizierung

Erzeuge ein Token unter Einstellungen → Sicherheit → API-Tokens. Jedes Token ist auf ausgewählte Boards beschränkt und wird nur einmalig im Klartext angezeigt.

Authorization: Bearer flz_dein_token

Token-Anfragen brauchen kein CSRF-Token. Session-Anfragen aus dem Browser brauchen bei schreibenden Aktionen den Header X-CSRF-Token (via GET /api/csrf).

Rechte eines Tokens

Endpunkte

Konto

GET/api/csrf

CSRF-Token für schreibende Session-Anfragen: { "token": "…" }.

POST/api/auth/register

Registrieren. Body: { "email": "…", "password": "…" } (≥8). Legt Konto an und meldet an (Session-Cookie).

POST/api/auth/login

Anmelden. Body: { "email": "…", "password": "…" }. Setzt Session-Cookie. 401 bei falschen Daten (rate-limited).

POST/api/auth/logoutnur Session

Abmelden (Session beenden). → 204.

GET/api/auth/me

Angemeldeter Nutzer: { "user": { id, email } }. 401 wenn keine Session.

POST/api/auth/passwordnur Session

Passwort ändern. Body: { "current_password": "…", "new_password": "…" } (neu ≥8). → 204. Fehler: 403 invalid_current, 422 weak_password. Nicht per API-Token (Account-Aktion).

Boards

GET/api/boards

Liste der Boards (Token: nur freigegebene).

GET/api/boards/{id}

Board inkl. Spalten und Karten (verschachtelt), plus default_column_id, default_view (board|calendar) und die eigene Rolle role (owner|editor|viewer). Enthält außerdem gruppen[] und themenbloecke[] (Unterrichtsplanung, s. u.). Archivierte Karten sind ausgeblendet. Nur für Mitglieder.

POST/api/boardsnur Session

Board anlegen. Body: { "title": "…" }. Legt 3 Standard-Spalten an.

PATCH/api/boards/{id}

Umbenennen, Standard-Spalte und/oder Standard-Ansicht setzen. Body: { "title"?: "…", "default_column_id"?: 12, "default_view"?: "board|calendar", "bundesland"?: "BY" }. default_view = Startansicht des Boards (nur Owner). bundesland = 2-Buchstaben-Kürzel (BW,BY,BE,BB,HB,HH,HE,MV,NI,NW,RP,SL,SN,ST,SH,TH) oder ""/null = keins; steuert die Feiertags-Anzeige in Kalenderboards.

DELETE/api/boards/{id}

Board löschen (Soft-Delete).

PATCH/api/boards/reorder

Reihenfolge der Boards in der eigenen Seitenleiste setzen. Body: { "order": [12, 4, 7] } (Board-IDs in gewünschter Reihenfolge). Pro Nutzer; wirkt nur auf eigene Mitgliedschaften.

GET/api/boards/{id}/stream

Server-Sent-Events-Strom für Live-Updates (nur Mitglied). Sendet bei Board-Änderungen ein message-Event; der Client lädt das Board dann neu. Heartbeats halten die Verbindung offen.

GET/api/boards/{id}/audit

Änderungsverlauf des Boards: [{ id, action, card_id, payload, user_email, created_at }] (max 100, neueste zuerst). Aktionen: board./column./card.* (create/update/move/delete/archive/unarchive), themenblock.*, gruppe.*.

GET/api/boards/{id}/archive

Archivierte Karten des Boards: [{ id, board_id, column_id, column_title, title, description, priority, due_date, archived_at }] (neueste zuerst). Archiviert wird über PATCH /api/cards/{id} mit { "archived": true }, wiederhergestellt mit false.

Mitglieder / Sharing

GET/api/boards/{id}/membersnur Session · Mitglied

Mitglieder des Boards: [{ user_id, email, role, created_at }]. role = owner|editor|viewer. Lesbar für alle Mitglieder (für Assignee-Auswahl); Verwalten nur Owner.

POST/api/boards/{id}/membersnur Session · Owner

Registrierten Nutzer hinzufügen. Body: { "email": "…", "role": "editor|viewer" }. 404 wenn nicht registriert, 409 wenn schon Mitglied.

PATCH/api/boards/{id}/members/{userId}nur Session · Owner

Rolle ändern. Body: { "role": "editor|viewer" }. Owner ist geschützt.

DELETE/api/boards/{id}/members/{userId}nur Session · Owner

Mitglied entfernen (Owner geschützt).

Zugriff nach Rolle (aktiv): Ein Nutzer sieht/bearbeitet ein Board nur als Mitglied. viewer = nur lesen (schreibende Endpunkte → 403), editor = Karten/Spalten bearbeiten, owner = zusätzlich Board umbenennen/löschen, Standard-Spalte, Mitglieder verwalten. GET /api/boards/{id} liefert die eigene Rolle als board.role. API-Token wirken mit der Rolle ihres Nutzers auf den freigegebenen Boards.

Webhooks

GET/api/boards/{id}/webhooksnur Session · Owner

Webhooks des Boards: [{ id, url, events, active, has_secret, last_status, last_at }] plus events (Liste aller möglichen Event-Namen).

POST/api/boards/{id}/webhooksnur Session · Owner

Webhook anlegen. Body: { "url": "https://…", "events"?: "card.create,card.move", "secret"?: "…", "active"?: true }. events = CSV bekannter Events oder leer (= alle). secret optional (HMAC-Signatur).

PATCH/api/boards/{id}/webhooks/{whId}nur Session · Owner

Ändern: { "url"?, "events"?, "secret"?, "active"? }.

DELETE/api/boards/{id}/webhooks/{whId}nur Session · Owner

Webhook löschen.

Zustellung: Bei einem Event sendet Fluss POST an jede aktive, passende URL. Body: { event, board_id, card_id, payload, occurred_at }. Events: board.update/delete, column.create/update/delete, card.create/update/move/delete, card.archive/unarchive, themenblock.create/update/delete. Mit secret: Header X-Fluss-Signature: sha256=<HMAC-SHA256 des Bodys>. Zustellung asynchron, Best-effort (Timeout 4s); der letzte HTTP-Status steht in last_status. Ziele im privaten/loopback-Netz werden aus Sicherheitsgründen (SSRF) blockiert (last_status = 0).

Spalten

POST/api/columns

Spalte am Ende anlegen. Body: { "board_id": 4, "title": "Review" }.

PATCH/api/columns/{id}

Umbenennen, verschieben, Farbe und/oder Bemerkung setzen. Body: { "title"?: "…", "position"?: 1, "color"?: "#ff8800", "note"?: "…" }. color = Hex #rrggbb oder ""/null (automatisch). note = Text (≤2000) oder ""/null. Spalten im Board-View enthalten color und note.

DELETE/api/columns/{id}

Spalte löschen (inkl. enthaltener Karten).

Karten

GET/api/cards/{id}

Einzelne Karte mit allen Eigenschaften: id, board_id, column_id, title, description, assignee, assignee_user_id, position, priority, due_date, starts_at, ends_at, created_at, updated_at, tags[], links[], comment_count und den vollständigen comments[] (im Board-View /api/boards/{id} nur comment_count, nicht die Kommentare).

POST/api/cards

Karte anlegen. Body: { "column_id"?: 8, "board_id"?: 4, "title": "…", "priority"?: "hoch", "due_date"?: "2026-07-10", "starts_at"?: "2026-09-01 09:00", "ends_at"?: "2026-09-01 12:30", "description"?: "…", "assignee_user_id"?: 33, "tags"?: ["Bug","Technik"], "links"?: [{"url":"https://…","label":"Doku"}] }. Ohne column_id landet die Karte in der Standard-Spalte des Boards. starts_at/ends_at = Zeitspanne (getrennt von der Frist due_date); ends_at darf nicht vor starts_at liegen (sonst 422). assignee_user_id = Board-Mitglied (muss Mitglied sein, sonst 422; leer/null = niemand); Legacy-Freitext assignee weiterhin akzeptiert. tags akzeptiert Array oder Komma-String; links nur http(s).

PATCH/api/cards/{id}

Ändern und/oder verschieben. Body: { "title"?, "description"?, "assignee_user_id"?, "tags"?, "links"?, "priority"?, "due_date"?, "starts_at"?, "ends_at"?, "column_id"?, "position"? }. starts_at/ends_at: ""/null entfernt die Zeit. tags und links ersetzen die jeweilige Menge komplett ([] löscht), assignee_user_id: ""/null entfernt die Zuständigkeit. Karten liefern assignee (E-Mail des Nutzers oder Legacy-Text) und assignee_user_id. { "archived": true|false } archiviert die Karte bzw. stellt sie wieder her (archivierte Karten sind aus Board, Kalender und Suche ausgeblendet, s. GET /api/boards/{id}/archive). { "target_board_id": 12 } verschiebt die Karte auf ein anderes Board (in dessen Standard-Spalte); erfordert Editor-Rechte auf Quell- UND Zielboard, sonst 403. Terminal — andere Felder werden dann ignoriert.

DELETE/api/cards/{id}

Karte löschen (Soft-Delete).

Kommentare

GET/api/cards/{id}/comments

Kommentare einer Karte: [{ id, author, author_type, body, created_at }]. author_type = user (E-Mail) oder token (Token-Name).

POST/api/cards/{id}/comments

Kommentar anlegen. Body: { "body": "…" }. Autor wird automatisch aus dem Zugang abgeleitet (Session → E-Mail, Token → Token-Name).

DELETE/api/comments/{id}

Kommentar löschen.

Suche

GET/api/search?q=…

Volltextsuche über die Karten des Nutzers (Token: nur Scope-Boards) in Titel, Beschreibung, Zuständig und Tags. → { "query": "…", "results": [ { id, board_id, board_title, column_id, title, description, assignee, priority, due_date } ] } (max 50, neueste zuerst). Leeres q → leere Liste.

Unterrichtsplanung — Themenblöcke

Zweistufiges Planungsmodell auf dem Board-Kalender: Themenblöcke (zeitliche Container) gehören je einer Gruppe an und werden mit Inhalten (Themen/Aufgaben/Tests …) gefüllt. Der Board-View (GET /api/boards/{id}) liefert gruppen[] und themenbloecke[] mit.

GET/api/themenbloecke?board_id=4

Themenblöcke eines Boards: [{ id, board_id, gruppe_id, titel, beschreibung, starts_at, ends_at, stunden, stunden_manuell, farbe, verantwortlich, position, inhalt_count }].

GET/api/themenbloecke/{id}

Einzelner Themenblock.

POST/api/themenbloecke

Anlegen. Body: { "board_id": 4, "gruppe_id": 7, "titel": "…", "starts_at": "2026-09-01 08:00", "ends_at": "2026-09-05 15:00", "beschreibung"?: "…", "stunden"?: 35, "farbe"?: "#3b7e9b", "verantwortlich"?: "…" }. gruppe_id ist Pflicht (422 gruppe_required). Der Datumsbereich muss im Zeitraum der Gruppe liegen (422 outside_gruppe). stunden leer/weggelassen = automatisch: Werktage (Mo–Fr) × tägliches Zeitfenster (End- minus Start-Uhrzeit); ein Wert überschreibt (stunden_manuell=true). farbe leer = erbt Gruppen-/Standardfarbe.

PATCH/api/themenbloecke/{id}

Ändern/verschieben. Body wie POST (alle Felder optional). gruppe_id kann nicht auf leer gesetzt werden. Zeitraum-Prüfung gegen die Gruppe gilt weiterhin.

DELETE/api/themenbloecke/{id}

Themenblock löschen (Soft-Delete).

Unterrichtsplanung — Gruppen

GET/api/gruppen?board_id=4

Gruppen eines Boards: [{ id, board_id, name, color, starts_at, ends_at, position }].

POST/api/gruppen

Anlegen. Body: { "board_id": 4, "name": "…", "color"?: "#5cb2af", "starts_at"?: "2026-09-01", "ends_at"?: "2027-06-30" }. Start/Ende (Datum) optional; leer = unbegrenzt.

PATCH/api/gruppen/{id}

Ändern: { "name"?, "color"?, "starts_at"?, "ends_at"?, "position"? }.

DELETE/api/gruppen/{id}

Gruppe löschen. 422 gruppe_has_blocks, wenn noch Themenblöcke daran hängen (erst diese verschieben/löschen).

Unterrichtsplanung — Inhalte (Step 2)

GET/api/themenbloecke/{id}/inhalte

Inhalte eines Blocks: [{ id, themenblock_id, art, titel, beschreibung, erledigt, position, created_at, updated_at }].

POST/api/themenbloecke/{id}/inhalte

Anlegen. Body: { "titel": "…", "art"?: "thema|aufgabe|test|material|lernziel", "beschreibung"?: "…" }. art Standard thema.

PATCH/api/inhalte/{id}

Ändern: { "titel"?, "art"?, "beschreibung"?, "erledigt"?: true|false, "position"? }.

DELETE/api/inhalte/{id}

Inhalt löschen.

Batch

POST/api/batch

Mehrere Operationen in einem Request. Body: { "operations": [ { "method": "POST|PATCH|DELETE|GET", "path": "/api/…", "body": {…} } ] } (max 50). Jede Op wird intern wie ein Einzel-Request behandelt (Auth, Board-Scope, Validierung gelten pro Op). Antwort: { "results": [ { "status": 201, "body": {…} } ] } in Eingabe-Reihenfolge. Nicht transaktional — Ops laufen sequenziell, Fehler einzelner Ops brechen den Batch nicht ab. CSRF nur einmal für den Batch (Token: keins). /api/batch selbst ist als Sub-Op nicht erlaubt.

API-Tokens

GET/api/tokensnur Session

Eigene Tokens: [{ id, name, prefix, boards:[{id,title}], last_used_at, created_at }]. Der Klartext-Token wird nie erneut angezeigt. Board-Scope umfasst nur eigene Boards.

POST/api/tokensnur Session

Token anlegen. Body: { "name": "…", "board_ids": [4,12] }. Antwort einmalig: { id, token, prefix, name }token danach nicht mehr abrufbar.

PATCH/api/tokens/{id}nur Session

Name und/oder Board-Scope ändern: { "name"?: "…", "board_ids"?: [4] }.

DELETE/api/tokens/{id}nur Session

Token widerrufen.

Werte

Beispiele

Karte in die Standard-Spalte eines Boards schreiben:

curl -X POST https://work.seolizer.de/api/cards \
  -H "Authorization: Bearer flz_dein_token" \
  -H "Content-Type: application/json" \
  -d '{"board_id": 4, "title": "Neue Aufgabe", "priority": "hoch", "due_date": "2026-07-10"}'

Board mit Spalten und Karten auslesen:

curl https://work.seolizer.de/api/boards/4 \
  -H "Authorization: Bearer flz_dein_token"

Karte in eine andere Spalte verschieben (an Position 0):

curl -X PATCH https://work.seolizer.de/api/cards/42 \
  -H "Authorization: Bearer flz_dein_token" \
  -H "Content-Type: application/json" \
  -d '{"column_id": 9, "position": 0}'