REST-API
Basis: /api/v1. Antworten sind JSON; Fehler haben die Form {"error": "Text"}. Authentifizierung über Authorization: Bearer <JWT>. API-Keys (Bearer mindsift_sec_… bzw. Header X-API-Key) liefern nur Daten ein, über POST /inbox und POST /webhooks/ingest, und nur für die Ziele ihrer Scopes: webhook:triage (Inbox), webhook:task (Aufgaben), webhook:list (Listeneinträge). Alles andere, auch Live-Streams, antwortet mit 403 bzw. 401. Zeitstempel sind ISO-8601 (UTC). Quelle der Wahrheit sind die Routen in server.go.
Öffentlich
| Methode |
Pfad |
Zweck |
| GET |
/health |
Lebenszeichen |
| GET |
/auth/status |
needsSetup, allowRegistration, instanceName, demoMode, sharingMode, legal (imprintUrl, hasImprintContent, privacyUrl, hasPrivacyContent) |
| GET |
/legal/{imprint|privacy} |
Hinterlegter Markdown-Text (content), ohne Anmeldung; 404, wenn keiner gesetzt ist |
| POST |
/auth/setup |
Erstes Administratorkonto (email, password, displayName, instanceName, allowRegistration, sharingMode, seedSampleData); nur solange es keine Konten gibt |
| POST |
/auth/register |
Registrierung (email, password, displayName, inviteCode); nur mit Code oder wenn freigegeben |
| POST |
/auth/login |
Anmeldung (login/email/username, password) → {token, user} |
| POST |
/auth/forgot-password |
{email} → immer {ok: true}; Mail mit Link nur, wenn das Konto existiert. 503, wenn SMTP oder BASE_URL fehlen |
| POST |
/auth/reset-password |
{token, password} (mind. 8 Zeichen) → neues Passwort, alle Sitzungen beendet; 400 bei ungültigem oder abgelaufenem Link |
| POST |
/auth/quickstart |
Immer 403 (Gastzugang deaktiviert) |
| POST |
/webhooks/ingest |
Einliefern per Webhook, Key als X-API-Key (empfohlen), Bearer oder ?token=. Body: title/content/text/message, priority, source, tags, dueDate; target (oder type): triage (Standard, Inbox), task (optional taskListId), list_item (listId, category, quantity). Klartext-Body wird als Inhalt genommen. 403 ohne passenden Scope oder ohne Schreibrecht auf die Liste |
| POST |
/auth/stream-ticket |
Einmal-Ticket (60 s) für WebSocket/SSE |
| GET |
/ws, /ws/spaces/{id}, /ws/lists/{id} |
WebSocket, siehe websocket.md; ?ticket= oder Authorization-Header |
| GET |
/events |
SSE-Fallback; ?ticket= oder Authorization-Header |
Konto und Allgemeines (angemeldet)
| Methode |
Pfad |
Zweck |
| GET |
/auth/me |
Benutzer und Kennzahlen |
| POST |
/events/rooms |
SSE-Raum beitreten/verlassen (sseId, room, action); nur eigene sseId, nur Räume mit Lesezugriff |
| POST |
/auth/logout |
Token sperren |
| POST |
/auth/revoke-all |
Alle Sitzungen beenden (token_version +1) |
| PATCH |
/auth/settings |
displayName, email, password, avatarUrl, alarmChannels, alarmEmail, discordWebhookUrl, customWebhookUrl, notifyBeforeMinutes |
| POST |
/auth/alarms/test |
Test-Alarm (channel: email/discord/webhook, optional target) |
| GET |
/users/directory, /collaborators |
Personen, die man sehen und mit denen man teilen kann (je nach sharingMode, sharing-and-permissions.md) |
| GET |
/stats |
Zähler fürs Dashboard (u. a. inboxCount, tasksOpenCount, listItemsOpenCount = offene Einträge aller sichtbaren Listen) |
| GET |
/search?q= |
Globale FTS5-Suche in zugänglichen Notizen (Titel & Blöcke), Aufgaben und Listen → {thoughts, documents, listItems, hits} |
| GET |
/export |
Datenexport als JSON; mit ?format=zip dieselben Daten als mindsift-backup.json in einer ZIP (so lädt die Oberfläche das Backup) |
| GET |
/export/markdown |
Obsidian-kompatibles ZIP (mindsift-vault-DATUM.zip), direkt gestreamt: Notizen als .md mit YAML-Frontmatter in der Ordnerstruktur des Nutzers (inkl. geteilter), Aufgaben/<Liste>.md im Format des Tasks-Plugins (📅, ⏫/🔺), Listen/<Liste>.md |
| GET/POST |
/themes |
Eigene CSS-Themes; anlegen oder mit id ändern (name, css, max. 200 KB) |
| DELETE |
/themes/{id} |
Eigenes Theme löschen |
| POST |
/import/mindsift, /import/obsidian |
Import (Multipart-Feld file, max. 100 MB): MindSift-JSON/-Archiv bzw. Obsidian-Vault-ZIP; ?preview=1 liefert nur die Zusammenfassung (folders, notes, taskLists, tasks, lists, listItems, inbox, skipped) |
| — |
/import/mindsift?mode=restore |
Wiederherstellen: löscht vor dem Import alle eigenen Notizen, Ordner, Aufgaben, Gedanken und Listen (erst nachdem die Datei vollständig gelesen und geprüft ist) |
| DELETE |
/user/data |
Alle eigenen Daten löschen, Konto bleibt ({"password"} nötig) |
| DELETE |
/user/account |
Eigenes Konto mit allen Daten löschen ({"password"} nötig); 403 für den letzten Server-Admin. /auth/me liefert dafür isLastAdmin |
| GET/POST/DELETE |
/keys, /keys/{id} |
API-Keys verwalten (Klartext nur bei Erstellung); POST mit name und scopes (Standard ["webhook:triage"]) |
Globale Suche (GET /api/v1/search?q=)
Führt eine Volltextsuche über SQLite FTS5 mit BM25-Relevanz-Ranking aus.
- Durchsucht Aufgaben/Gedanken (Inhalt, Notizen, Unteraufgaben), Notizen (Titel sowie Inhalte aktiver Blöcke) und Listeneinträge.
- Beachtet strikt alle Zugriffsregeln: Nur eigene Gedanken/Aufgaben und solche in zugänglichen Aufgabenlisten; nur eigene und geteilte Notizen (direkt, über Gruppen oder freigegebene Ordner); nur eigene und geteilte Listen.
- Titel-Treffer werden höher gewichtet als Treffer im Fließtext.
- Liefert rückwärtskompatible Collections (
thoughts, documents, listItems) sowie strukturierte hits mit Snippet und <mark>-Hervorhebung:
Gedanken, Triage, Aufgaben
| Methode |
Pfad |
Zweck |
| POST |
/inbox |
Schnellerfassung. Body: content, priority, source, dueDate, target (inbox/task/list_item/document), targetListId, taskListId (snake_case-Varianten werden akzeptiert) |
| GET |
/thoughts?status= |
Gedanken (inbox, kept, converted, discarded, all) |
| GET/PATCH/DELETE |
/thoughts/{id} |
Einzelabruf; Teiländerung (content, isCompleted, dueDate, priority, taskListId, kanbanStatus, notes, subtasks, tags, assignees, recurrence = daily/weekly/monthly/yearly/leer); Löschen. assignees ersetzt die Zuweisungen; nur Personen mit Zugriff auf die Liste, sonst 403 |
| DELETE |
/thoughts/discarded/purge |
Papierkorb leeren |
| GET |
/triage/deck |
Posteingang (max. 100) |
| PATCH |
/triage/{id} |
action: keep/discard/restore; mit convertTo: task/document/list_item |
| GET |
/tasks?taskListId= |
Aufgaben (optional einer Liste) |
| POST |
/tasks/reorder, /task-lists/{id}/tasks/reorder |
Reihenfolge/Spalte ändern (items[{id, sortOrder, kanbanStatus}]) |
| POST |
/tasks/{id}/unlink |
Verknüpfung zu Notiz-Block lösen |
Aufgabenlisten
| Methode |
Pfad |
Zweck |
| GET/POST |
/task-lists |
Listen (permission je Liste: owner/editor/viewer); anlegen (title, icon, color, teamId) |
| PUT/DELETE |
/task-lists/{id} |
Ändern (title, icon, color, teamId, columns) – nicht für Leser; Löschen nur Eigentümer |
| GET |
/task-lists/{id}/shares |
Freigaben |
| POST |
/task-lists/{id}/shares |
Nur Eigentümer. Body: targetType (team/user), targetId, permission (editor/viewer, Standard editor). Erneutes Senden für dasselbe Ziel ändert die Stufe |
| DELETE |
/task-lists/{id}/shares/{shareId} |
Nur Eigentümer |
Schreibzugriffe von Lesern auf Listen oder deren Aufgaben liefern 403 („Keine Schreibrechte für diese Aufgabenliste (nur Lesen)“).
Workspace
| Methode |
Pfad |
Zweck |
| GET/POST/DELETE |
/folders |
Ordnerliste, anlegen (path), löschen |
| POST |
/folders/move |
sourcePath, targetParentPath |
| GET/POST |
/workspace/folders/shared |
Geteilte Ordner; anlegen (name, mountPath) |
| GET/DELETE |
/workspace/folders/shared/{id} |
Abruf; Löschen |
| PUT |
/workspace/folders/shared/{id}/mount |
Eigenen Einhängepfad ändern (mountPath) |
| GET/POST/DELETE |
/workspace/folders/shared/{id}/shares[/{shareId}] |
Freigaben (permission: read/write) |
| GET |
/workspace/tags, /workspace/tags/{tag} |
Tag-Übersicht und -Details |
| PUT |
/workspace/tags/{tag}/note |
Tag-Seite speichern (content) |
| GET/POST |
/documents |
Notizen auflisten; anlegen (title, icon, folderPath, blocks, tags, isPinned) |
| GET/PUT/DELETE |
/documents/{id} |
Abruf; Ändern von title, icon, folderPath, isPinned (Blöcke → 400, sie laufen über /docs/{id}/sync); Löschen = in den Papierkorb (Eigentümer) bzw. Freigabe verlassen (andere) |
| GET/DELETE |
/documents/trash |
Papierkorb auflisten (id, title, icon, folderPath, deletedAt, isOwner) bzw. leeren |
| GET |
/documents/{id}/backlinks |
Notizen mit [[Titel]]-Link auf diese Notiz (nur sichtbare, ohne Papierkorb): docId, title, icon, folderPath, bis zu 5 blocks (id, content), more |
| POST |
/documents/{id}/restore |
Aus dem Papierkorb zurückholen (fehlender Ordner wird neu angelegt) |
| DELETE |
/documents/{id}/permanent |
Notiz im Papierkorb endgültig löschen |
| PUT |
/documents/{id}/move |
Ordner wechseln (folderPath) |
| POST |
/documents/{id}/blocks/{blockId}/enhanced-task |
Aus einem Block eine verknüpfte Aufgabe machen (content, checked) |
| GET/POST/DELETE |
/documents/{id}/shares[/{userId}] |
Notiz-Freigaben (userId/email/teamId, permission: read/write) |
Der Inhalt einer Notiz (Blöcke) wird ausschließlich über POST /docs/{id}/sync geändert. POST /documents nimmt beim Anlegen weiterhin eine Start-Blockliste an; sie wird intern in Operationen umgerechnet.
Gemeinsames Bearbeiten von Notizen
Konzept und Regeln: modules/collaboration.md. Zugriff über die Rolle owner/editor/viewer (Store.DocRole); ohne Rolle 404.
| Methode |
Pfad |
Zweck |
| GET |
/docs/{id}/snapshot |
Kompletter Blockbaum inkl. Grabsteinen, title, titleVersion, headSeq, role, shared |
| POST |
/docs/{id}/sync |
Ein Rundlauf: eigene Operationen senden, Leases halten/abgeben, Log ab lastSeq abholen |
| GET |
/docs/{id}/changes?since=N&wait=S |
Long-Polling: antwortet sofort, wenn headSeq > N, sonst bei der nächsten Änderung (auch Leases/Präsenz) oder nach höchstens 25 s. Antwort {headSeq, changed}; Daten holt der Client danach per /sync |
| POST |
/docs/{id}/leave |
Sitzung beenden, alle Leases des Clients freigeben ({clientId}) |
/sync-Anfrage:
- Operationsarten:
insert, set_content, set_props (patch: type, checked, collapsed, lang, clearConflict), move, delete, restore (ids), set_title (baseVersion, title).
leases.hold ist die vollständige Liste der Blöcke, die der Client halten will; alle anderen Leases des Clients werden freigegeben. Neu eingefügte Blöcke und Konfliktkopien bekommt der Autor automatisch.
- Höchstens 500 Operationen pro Anfrage. Leser dürfen keine Operationen senden (
403), aber abfragen.
skipLog: true liefert kein Log zurück; gedacht für einzelne Änderungen ohne geöffneten Editor (z. B. Abhaken in der To-Do-Übersicht).
Antwort: results (je Operation status applied/conflict/rejected, ggf. conflictBlockId, version, seq), ops (Log-Einträge {seq, opId, userId, clientId, effect} ab lastSeq, höchstens 1000; hasMore bei mehr), headSeq, resync (Snapshot neu laden: der Client ist dem Server voraus, oder sein lastSeq liegt vor der verdichteten Grenze log_floor; die gesendeten Operationen werden trotzdem angewendet), role, shared, sessions, presence, leases (granted, denied, others).
Wiederholte Operationen (gleiche opId) werden nicht erneut angewendet; die Antwort enthält das gespeicherte Ergebnis.
Geteilte Listen
| Methode |
Pfad |
Zweck |
| GET/POST |
/lists |
Listen; anlegen (title, icon, listType) |
| GET/DELETE |
/lists/{id} |
Abruf mit Einträgen; Löschen |
| GET/POST/DELETE |
/lists/{id}/shares[/{shareId}] |
Freigaben (targetType, targetId, role: editor/viewer) |
| POST |
/lists/{id}/items |
Eintrag (content, category, quantity) |
| PATCH |
/lists/{id}/items/{itemId} |
Abhaken (isCompleted) |
| PUT |
/lists/{id}/items/{itemId} |
Ändern (content, category, quantity) |
| DELETE |
/lists/{id}/items/{itemId} |
Löschen |
| POST/PUT |
/lists/{id}/items/reorder |
Reihenfolge/Kategorie (items[{id, sortOrder, category, content}]) |
| POST |
/lists/{id}/clear-completed |
Erledigte entfernen |
Gruppen
| Methode |
Pfad |
Zweck |
| GET/POST |
/teams |
Eigene Gruppen; anlegen (name, description) – Ersteller wird Gruppen-Admin, die Gruppe gehört zu seiner Organisation; im Modus private gesperrt |
| PUT/DELETE |
/teams/{id} |
Nur Gruppen-Admin |
| POST |
/teams/{id}/members |
Mitglied hinzufügen (userId oder email, role); nur sichtbare Personen, sonst 404 |
| PUT/DELETE |
/teams/{id}/members/{userId} |
Rolle ändern (admin/member); entfernen |
Administration (role = admin)
| Methode |
Pfad |
Zweck |
| GET/POST |
/admin/users |
Benutzer auflisten, anlegen (email, password, displayName, role, optional organizationId) |
| PUT |
/admin/users/{id}/organization |
Organisation setzen (organizationId, leer = keine); entzieht Freigaben, Gruppen und Zuweisungen der alten |
| GET/POST |
/admin/organizations |
Organisationen mit memberCount; anlegen (name) |
| PUT/DELETE |
/admin/organizations/{id} |
Umbenennen (name); löschen (Mitglieder sind danach ohne Organisation) |
| PATCH/DELETE |
/admin/users/{id} |
displayName, email, role, isActive, discordWebhookUrl; löschen |
| PUT |
/admin/users/{id}/password |
Passwort zurücksetzen |
| GET/POST/DELETE |
/admin/invites[/{id}] |
Einladungen (email, role, organizationId, validDays) |
| GET/PUT |
/admin/settings |
instanceName, allowRegistration, sharingMode (private/organizations/open), SMTP-Felder; ein Moduswechsel bereinigt Freigaben |
| GET |
/admin/settings/sharing-impact?mode= |
Probelauf: wie viele Freigaben und Gruppen-Mitgliedschaften ein Wechsel entfernen würde (affected) |
| POST |
/admin/smtp/test |
Testmail (toEmail) |
Beispiele