MindSift now speaks MCP (Streamable HTTP, stateless JSON-RPC) at POST /api/v1/mcp so Claude Code, Codex, Claude Desktop, Cursor and custom agents can read, create and complete tasks and keep shared lists. API keys get the scopes tasks:read/write and lists:read/write; tools/list only shows what the key may call and every tool checks its scope. Task and list write paths are shared helpers (updateTask, createTask, addListItem, setListItemDone) used by REST and MCP alike. Settings: scope selection and the SectionPicker (import, restore) use ToggleSwitch rows instead of checkbox-like buttons; new MCP section with connection snippets for Claude Code, Codex and JSON configs. Docs, README, help and changelog updated; v0.28.0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
25 KiB
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 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). Mit den Agenten-Scopes tasks:read, tasks:write, lists:read, lists:write nutzen sie außerdem den MCP-Endpunkt. Alles andere, auch Live-Streams, antwortet mit 403 bzw. 401. Zeitstempel sind ISO-8601 (UTC). Anfragekörper sind auf 2 MB begrenzt (Notiz-Sync 8 MB, Importe 101 MB). Im Demo-Modus antworten Endpunkte, die etwas verschicken, Server-Einstellungen ändern oder die gemeinsamen Demo-Konten verändern würden, mit 403 „In der Demo deaktiviert“. 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, customLinkLabel, customLinkUrl) |
| GET | /legal/{imprint|privacy} |
Hinterlegter Markdown-Text (content), ohne Anmeldung; 404, wenn keiner gesetzt ist |
| POST | /share-target |
PWA Web Share Target Fallback: Weiterleitung auf / (303 See Other), falls Service Worker noch inaktiv |
| POST | /auth/setup |
Erstes Administratorkonto (email, password, displayName, instanceName, allowRegistration, sharingMode, seedSampleData); nur solange es keine Konten gibt |
| POST | /auth/verify-email |
Link aus der Bestätigungs-E-Mail (token): schaltet das Konto frei oder reicht es an die Freigabe weiter ({"verified","pending","message"}) |
| POST | /auth/resend-verification |
Bestätigungslink erneut schicken (email); antwortet immer gleich, höchstens alle 2 Minuten pro Konto |
| POST | /auth/register |
Registrierung (email, password, displayName, inviteCode); mit Code immer, sonst je nach Registrierungsmodus. 201 mit Token oder 202 {"pending":true,"verify","message"}, wenn erst die E-Mail-Adresse bestätigt (verify) und/oder ein Admin freigeben muss (Login bis dahin 403); 429 nach 20 Versuchen pro Adresse in 15 Minuten oder bei mehr als 50 wartenden Konten |
| 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; 429 nach 10 Anfragen pro Adresse in 15 Minuten |
| 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= (in der URL nur API-Keys, keine Sitzungs-Tokens). 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 |
| 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/stream-ticket |
Einmal-Ticket (60 s) für WebSocket/SSE |
| POST | /voice/transcribe |
Nur im Server-Modus der Spracherkennung: multipart/form-data mit file (Aufnahme, z. B. WebM/Opus oder MP4/AAC) → {text}. 404 ohne STT_URL, 413 zu groß, 429 Stundenlimit, 503 ausgelastet, 502 Whisper-Fehler (Details im Log) |
| POST | /auth/logout |
Sitzung dieses Geräts beenden |
| GET | /auth/sessions |
Angemeldete Geräte: [{id, createdAt, lastUsedAt, userAgent, ip, current}] |
| DELETE | /auth/sessions/{id} |
Ein Gerät abmelden; 404 bei fremder oder unbekannter Sitzung |
| POST | /auth/revoke-all |
Alle Sitzungen beenden (token_version +1) |
| PATCH | /auth/settings |
displayName, email, password, avatarUrl, alarmChannels, alarmEmail, discordWebhookUrl, customWebhookUrl, notifyBeforeMinutes. notificationSettings (paused, includeOwnWatched, events.{due,shared,taskDone,watched}.{email,discord}, dailySummary.{enabled,time,email,discord}) für Benachrichtigungen. Neue email oder password nur mit currentPassword (403 bei falschem, 429 bei Login-Sperre); nach neuem Passwort sind andere Geräte abgemeldet, das neue Token kommt im Header X-Session-Token |
| 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 | /backups, /backups/{name} |
Eigene automatische Server-Backups auflisten bzw. herunterladen |
| POST | /backups/{name}/restore |
Server-Backup einspielen: mode=merge oder restore (gewählte Bereiche vorher leeren), sections=notes,tasks,lists,inbox,themes, preview=1 |
| GET/POST | /keys |
API-Keys auflisten bzw. anlegen (Klartext nur bei Erstellung); POST mit name und scopes (webhook:triage, webhook:task, webhook:list, tasks:read, tasks:write, lists:read, lists:write; Standard ["webhook:triage"]) |
| POST | /mcp |
MCP-Endpunkt für KI-Agenten, siehe unten |
| DELETE | /keys/{id} |
API-Key löschen |
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 strukturiertehitsmit Snippet und<mark>-Hervorhebung:{ "thoughts": [...], "documents": [...], "listItems": [...], "hits": [ { "type": "document", "id": "blk_123", "title": "Projektplanung", "snippet": "... wichtige Notiz zum <mark>Release</mark> ...", "docId": "doc_abc", "blockId": "blk_123" } ] }
MCP für KI-Agenten (POST /api/v1/mcp)
MindSift ist ein MCP-Server (Streamable HTTP, zustandslos, nur JSON-RPC 2.0 per POST; kein Server-Stream, GET antwortet 405, Batches werden abgelehnt). Anmeldung wie überall per X-API-Key oder Authorization: Bearer; eine Sitzung darf alles, ein API-Key nur die Werkzeuge seiner Scopes, und tools/list zeigt ihm nur diese. Protokollversionen 2025-06-18, 2025-03-26, 2024-11-05. Unterstützte Methoden: initialize, ping, tools/list, tools/call; Benachrichtigungen antworten 202. Fehler eines Werkzeugs kommen als isError-Ergebnis zurück, damit der Agent sie lesen kann; Ergebnisse stehen als structuredContent (Listen als {items}) und als JSON-Text.
| Werkzeug | Scope | Zweck |
|---|---|---|
list_task_lists |
tasks:read |
Aufgabenlisten mit permission und offenen Aufgaben |
list_tasks |
tasks:read |
Aufgaben (taskListId, status open/done/all, dueBefore JJJJ-MM-TT, limit); fällige zuerst |
get_task |
tasks:read |
Eine Aufgabe |
create_task |
tasks:write |
Aufgabe anlegen (content, taskListId, priority, dueDate, notes); Quelle agent |
update_task |
tasks:write |
Felder ändern (content, isCompleted, dueDate, priority, notes, kanbanStatus, taskListId, tags) |
complete_task |
tasks:write |
Abhaken (completed false = wieder öffnen) |
list_lists |
lists:read |
Geteilte Listen mit offenen Einträgen |
get_list |
lists:read |
Liste mit Einträgen |
add_list_item |
lists:write |
Eintrag hinzufügen (listId, content, category, quantity) |
check_list_item |
lists:write |
Eintrag abhaken (completed false = zurück) |
update_list_item |
lists:write |
Text, Kategorie, Menge ändern |
search |
ein Lese-Scope | Volltextsuche in Aufgaben und Listeneinträgen (nur die Teile der eigenen Scopes, keine Notizen) |
capture_to_inbox |
webhook:triage |
Gedanken in die Inbox |
Freigaben gelten wie für die Person selbst: Leser-Listen lassen sich nicht ändern, fremde Listen sind unsichtbar. Einrichtung z. B. in Claude Code: claude mcp add --transport http mindsift https://mindsift.example.com/api/v1/mcp --header "X-API-Key: mindsift_sec_…"; die Einstellungen zeigen die Varianten für Codex (config.toml) und JSON-Konfigurationen (Claude Desktop, Cursor). claude.ai und ChatGPT im Browser verlangen OAuth und werden noch nicht unterstützt.
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 |
| POST/DELETE | /tasks/{id}/watch |
Aufgabe beobachten bzw. nicht mehr → {watching}; 404 ohne Zugriff. Aufgaben-Antworten tragen isWatching |
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 | /workspace/folders/shared/{id}/shares |
Freigaben geteilter Ordner (permission: read/write) |
| DELETE | /workspace/folders/shared/{id}/shares/{shareId} |
Freigabe eines geteilten Ordners entfernen |
| 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 | /documents/{id}/shares |
Notiz-Freigaben (userId/email/teamId, permission: read/write) |
| DELETE | /documents/{id}/shares/{userId} |
Notiz-Freigabe entfernen |
| POST | /documents/{id}/extract |
{blockId, clientId}: legt aus den Unterpunkten des Punkts eine eigene Notiz an (Titel = Text ohne Hashtags, gleicher Ordner, Freigaben übernommen, fremder Besitzer bekommt Schreibrecht) → {document, link}. Die Ursprungsnotiz ändert der Server nicht; der Editor ersetzt den Ast selbst durch link (normale eigene Änderung, Strg+Z möglich). Schreibrecht nötig; 409, wenn jemand anderes im Ast schreibt; 400, wenn der Punkt nur noch ein Link auf eine Notiz ist (ggf. mit Hashtags) |
| GET | /push/key |
{enabled, publicKey} – öffentlicher VAPID-Schlüssel für pushManager.subscribe (in der Demo enabled: false) |
| GET | /push/devices |
Eigene Geräte mit Push → {devices: [{id, endpoint, device, createdAt}]} |
| POST | /push/subscribe |
{endpoint, keys: {p256dh, auth}, device} – Gerät anmelden (nur https-Endpunkte) |
| POST | /push/unsubscribe |
{endpoint} – Gerät abmelden |
| POST | /push/test |
Test-Benachrichtigung an alle eigenen Geräte → {devices}; 400 ohne Gerät |
| GET | /journal |
Tage des eigenen Journals, neueste zuerst → {days: [{date, docId, empty}]} (empty: Notiz noch ohne Text; Tage im Papierkorb fehlen) |
| POST | /journal/{date} |
Notiz des Tages (JJJJ-MM-TT) öffnen; gibt es noch keine, wird sie angelegt (Titel z. B. „Freitag, 30. Oktober 2026“, Icon 📅) → Dokument, 201 bei Neuanlage. Die Journal-Notizen sind normale Notizen (Teilen, Suche, Sync) |
| POST | /documents/{id}/extract/undo |
Legt die ausgelagerte Notiz {id} wieder in den Papierkorb (nur ihr Ersteller) |
| POST | /documents/{id}/merge |
{sourceId}: hängt die (eigene) Quellnotiz als Überschrift mit Unterpunkten an diese Notiz an, Quelle in den Papierkorb → {targetId, sourceId, sourceTitle, headingId} |
| POST | /documents/{id}/merge/undo |
{sourceId, headingId}: Abschnitt wieder entfernen, Quelle wiederherstellen |
| POST | /documents/{id}/media |
Bild oder Audio zu einer Notiz hochladen (multipart/form-data, Feld file; Schreibrecht nötig) → {id, kind, mime, size, url}. 415 falsches Format, 413 zu groß oder Kontingent voll |
| POST | /documents/{id}/media/fetch |
Externes Bild kopieren: {url} (nur https, keine internen Adressen; nicht in der Demo) → wie oben |
| GET | /media/{id} |
Datei abrufen (Leserecht auf eine Notiz, die sie zeigt), mit Range-Anfragen; sonst 404 |
| GET | /link-preview?url=https://… |
Vorschau einer Web-Seite (title, description, siteName, hasImage, hasIcon, ok, key), vom Server geholt und 7 Tage zwischengespeichert; 422 ohne https, 403 in der Demo, 429 zu viele Abrufe |
| GET | /link-preview/{key}/image bzw. /icon |
Bild bzw. Icon einer Vorschau |
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:
{
"clientId": "tab-7f3…",
"lastSeq": 412,
"ops": [
{ "opId": "c1a…", "kind": "set_content", "blockId": "b9…", "baseVersion": 7, "content": "Milch und Brot" },
{ "opId": "c1b…", "kind": "insert", "blockId": "bA…", "parentId": "b2…", "orderKey": "a0V", "type": "bullet", "content": "" }
],
"leases": { "hold": ["b9…", "bA…"], "release": [] }
}
- Operationsarten:
insert,set_content,set_props(patch:type,checked,collapsed,lang,clearConflict),move,delete,restore(ids),set_title(baseVersion,title). leases.holdist 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: trueliefert 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 | /lists/{id}/shares |
Freigaben abrufen bzw. anlegen (targetType, targetId, role: editor/viewer) |
| DELETE | /lists/{id}/shares/{shareId} |
Freigabe entfernen |
| 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 | /lists/{id}/items/{itemId}/move |
In eine andere Liste verschieben (targetListId); Bearbeitungsrecht in beiden Listen nötig, der Eintrag landet oben |
| 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 (bei wartenden Konten = ablehnen). GET /admin/users markiert wartende Konten mit pendingApproval |
| POST | /admin/users/{id}/approve |
Wartendes Konto freigeben; die Person bekommt eine E-Mail (409, wenn es nicht wartet) |
| PUT | /admin/users/{id}/password |
Passwort zurücksetzen |
| GET/POST | /admin/invites |
Einladungen auflisten bzw. anlegen (email, role, organizationId, validDays, maxUses 1–1000; mehrfach verwendbare nicht an eine E-Mail gebunden). Antwort mit maxUses/useCount. Als Link: /?invite=<CODE> öffnet die Registrierung mit eingetragenem Code |
| POST | /admin/invites/{id}/send |
Einladung per E-Mail (email); bindet einmalige Codes an diese Adresse; braucht SMTP |
| DELETE | /admin/invites/{id} |
Einladung widerrufen/löschen |
| GET/PUT | /admin/settings |
instanceName, allowRegistration, sharingMode (private/organizations/open), SMTP-Felder; ein Moduswechsel bereinigt Freigaben. smtpPass kommt nie zurück (smtpPassSet zeigt, ob eines gespeichert ist); leer = unverändert |
| 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) |
| GET | /admin/voice |
Aktive Spracherkennung (mode: server/browser/off), Einstellungen aus STT_* ohne Schlüssel, Grenzen und im Server-Modus ein frischer Zustand (status.reachable, status.modelReady) |
| GET/POST | /admin/backups |
Datenbank-Abzüge auflisten bzw. sofort sichern (Abzug + alle Benutzer-Backups) |
| GET | /admin/backups/{name} |
Datenbank-Abzug herunterladen |
| DELETE | /admin/backups/{name} |
Datenbank-Abzug löschen (gibt die verbleibende Liste zurück; nicht in der Demo) |
Beispiele
# Gedanken per API-Key erfassen
curl -X POST https://mindsift.example.com/api/v1/webhooks/ingest \
-H "X-API-Key: mindsift_sec_…" -H "Content-Type: application/json" \
-d '{"title":"Milch kaufen #einkauf","priority":"high","dueDate":"2026-11-01"}'
# Einkaufsliste per API-Key ergänzen (Scope webhook:list)
curl -X POST https://mindsift.example.com/api/v1/webhooks/ingest \
-H "X-API-Key: mindsift_sec_…" -H "Content-Type: application/json" \
-d '{"target":"list_item","listId":"lst_…","content":"Hafermilch","quantity":"2"}'
# Aufgabenliste nur lesend freigeben
curl -X POST https://mindsift.example.com/api/v1/task-lists/tkl_…/shares \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"targetType":"user","targetId":"usr_…","permission":"viewer"}'