Dokumente aktualisiert

This commit is contained in:
2026-04-16 11:11:48 +02:00
parent 0517241ee5
commit e76d80ece3
2 changed files with 255 additions and 485 deletions
+72 -75
View File
@@ -2,9 +2,9 @@
## Kontext ## Kontext
Dieses Projekt entwickelt `mcp-familywall` einen MCP-Server für den Lesezugriff Dieses Projekt entwickelt `mcp-familywall` einen MCP-Server für den
auf Family Wall (familywall.com). Der MCP-Server läuft lokal und wird in Claude Desktop Zugriff auf Family Wall (familywall.com). Der MCP-Server läuft lokal
eingebunden. und wird in Claude Desktop eingebunden.
## Infrastruktur ## Infrastruktur
@@ -20,48 +20,33 @@ eingebunden.
## Deploy-Workflow (nach jeder Code-Änderung) ## Deploy-Workflow (nach jeder Code-Änderung)
1. Claude Code committet und pusht (bei Berechtigungsfehler: bis zu 2 Retries, je 1s warten) 1. Claude Code committet und pusht (bei Berechtigungsfehler: bis zu 2 Retries, je 1s warten)
2. Marcus installiert lokal und startet Claude Desktop neu
## Aktueller Stand ## Aktueller Stand
### Implementierte Tools (v0.4.16) ### Implementierte Tools (v0.4.x)
| Kategorie | Tools | | Kategorie | Tools |
|---|---| |---|---|
| Kreise | `get_circles`, `get_members` | | Lesen | `get_circles`, `get_members`, `get_lists`, `get_tasks`, `get_categories`, `get_activities` |
| Listen | `get_lists` | | Tasks | `create_task`, `update_task`, `toggle_task`, `delete_task` |
| Tasks (Lesen) | `get_tasks` (inkl. `category_id`, `due_date`, `assignee_ids`), `get_categories` (inkl. `custom`-Flag) | | Kategorien | `create_category`, `delete_category` |
| Wall | `get_activities`, `like_post` | | Aktivitäten | `like_post` |
| Tasks (Schreiben) | `create_task` (inkl. `category_id`, `due_date`, `assignee_ids`), `update_task` (inkl. `category_id`, `due_date`, `clear_due_date`, `assignee_ids`, `list_id`), `toggle_task`, `delete_task` |
| Kategorien (Schreiben) | `create_category` (inkl. `icon`), `delete_category` (System-Kategorien geschützt) |
## Roadmap ## Roadmap
- v0.x: Erweiterter Lese- + Schreibzugriff ← aktuell - v0.4.x: Kategorie-Management, Task-Felder (due_date, assignee, list_id) ← aktuell
- Offen: Unlike (`like_post(like=False)`) - v0.5.x: Erinnerungen + Wiederholungen (Premium-Account erforderlich)
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
## Referenzprojekt
Im Ordner `reference/` liegen Dateien aus einem anderen MCP-Server-Projekt
als Orientierung für Struktur und Patterns:
| Datei | Zweck |
|---|---|
| `reference/pyproject.toml` | Projektstruktur, Dependencies, Entry-Points, Build-System |
| `reference/config.py` | Config-Pattern: YAML laden, validieren, speichern |
| `reference/auth.py` | Keyring-Integration, Credential-Resolution-Reihenfolge |
| `reference/cli.py` | Setup-Wizard, check, serve CLI-Struktur |
**Wichtig:** Diese Dateien sind Referenz, kein Copy-Paste. Alle
Synology/DSM-spezifischen Teile werden nicht übernommen.
Family Wall nutzt ein anderes Auth-Schema siehe SPEC.md.
## Architektur-Entscheidungen ## Architektur-Entscheidungen
### Session-Strategie ### Session-Strategie
Kein Session-Caching. Jeder Tool-Call führt Login → API-Call → Logout durch. Kein Session-Caching. Jeder Tool-Call führt Login → API-Call → Logout durch.
Credentials liegen im OS Keyring (nur `email` + `password`), kein `session_id`. Optimierung: Mehrere API-Calls in einer Session bündeln (login → call1 → call2 → logout)
um unnötige HTTP-Roundtrips zu vermeiden. Credentials liegen im OS Keyring
(nur `email` + `password`), kein `session_id`.
### Kreise (Scopes) ### Kreise (Scopes)
Family Wall kennt mehrere Kreise (z.B. Familie, erweiterter Familienkreis). Family Wall kennt mehrere Kreise (z.B. Familie, erweiterter Familienkreis).
@@ -72,68 +57,77 @@ Ohne `scope` werden alle Kreise zurückgegeben.
Systembezeichnungen (z.B. `SYS-CAT-SHOPPINGLIST`) werden in deutsche Systembezeichnungen (z.B. `SYS-CAT-SHOPPINGLIST`) werden in deutsche
Klarnamen übersetzt. Mapping-Tabelle in `modules/lists.py`. Klarnamen übersetzt. Mapping-Tabelle in `modules/lists.py`.
### Kategorien
- Kategorien sind family-wide, nicht list-spezifisch (`taskcategoryput` gilt für alle Listen)
- System-Kategorien: `rights.canDelete=null` → nicht löschbar
- Custom-Kategorien: `rights.canDelete="true"` → löschbar
- Locale-Filter: default `"de"` Custom-Kategorien haben kein locale-Feld
und werden immer angezeigt unabhängig vom Locale-Parameter
### Fehlerbehandlung
Die API gibt Fehler manchmal als `a00.un.un` zurück (nicht Top-Level).
`fw_client.py` prüft beides und wirft `FamilyWallError`. Nie silent-fail
als Erfolg werten immer `a00.un` und Top-Level `ex`/`un` prüfen.
### Service Worker
Die Family Wall Web-App nutzt einen Service Worker der bestimmte
HTTP-Requests abfängt und modifiziert. Browser-DevTools und JS-Interceptoren
können den echten Request-Body in diesen Fällen nicht sehen.
→ Immer `FW_DEBUG=1` für Traffic-Analyse nutzen, nicht Browser-DevTools.
## Claude Code Implementierungsregeln ## Claude Code Implementierungsregeln
- Keine destruktiven Operationen in v1.0 → kein Confirmation-Pattern erforderlich - **Feature complete before next feature** jedes Feature vollständig
implementieren, testen und verifizieren bevor das nächste beginnt
- **Kein destruktives Probing** keine Probe-Calls auf System-Kategorien,
echte Listen oder echte Tasks; immer Test-Objekte anlegen und danach
sofort löschen
- Fehlerbehandlung: API-Fehler als verständliche Meldung zurückgeben, keine Stacktraces - Fehlerbehandlung: API-Fehler als verständliche Meldung zurückgeben, keine Stacktraces
- Keine Secrets in stderr-Ausgaben (Passwort bei Debug-Logging maskieren) - Keine Secrets in stderr-Ausgaben (Passwort bei Debug-Logging maskieren)
- Type Hints und Docstrings konsequent verwenden - Type Hints und Docstrings konsequent verwenden (Englisch)
- Formatter: `ruff format`, Linter: `ruff check`, Tests: `pytest` - Formatter: `ruff format`, Linter: `ruff check`, Tests: `pytest`
- Alle Texte (Docstrings, Kommentare, README): Englisch - Alle Texte (Docstrings, Kommentare, README): Englisch
- Debug-Logging via `FW_DEBUG=1` Umgebungsvariable - Debug-Logging via `FW_DEBUG=1` Umgebungsvariable
- Nach jeder Aufgabe: git commit + push. Bei Berechtigungsfehler: 1s warten, bis zu 2 Retries - Nach jeder Aufgabe: git commit + push. Bei Berechtigungsfehler: 1s warten, bis zu 2 Retries
- .gitignore eigenständig pflegen (Credentials, __pycache__, .venv, .env, *.pyc etc.) - .gitignore eigenständig pflegen (Credentials, __pycache__, .venv, .env, *.pyc etc.)
- README.md im Projekt-Root pflegen und bei jedem Aufruf aktualisieren - README.md + CLAUDE.md im Projekt-Root bei jedem Aufruf aktualisieren
- Confirmation-Pattern in Docstrings: `IMPORTANT: Ask the user for confirmation
before calling this tool.` für destruktive oder schreibende Operationen
## Implementierungsreihenfolge ## Bekannte API-Eigenheiten und Fallstricke
### Gruppe 1 Projektgerüst + Auth + CLI ✦ Prio: hoch ### Sentinel-Wert `$empty`
Das FiZ-Framework nutzt `$empty` als Sentinel um optionale Felder zu löschen.
Normale Werte wie `""`, `null`, `"null"`, `"0"` werden vom Server abgelehnt.
Aktuell genutzt für: `dueDate=$empty` (Fälligkeitsdatum entfernen)
- `pyproject.toml`: Package `mcp-familywall`, Entry-Point `mcp-familywall` ### Silent-Fail via `a00.un.un`
- `src/mcp_familywall/config.py`: `~/.config/mcp-familywall/config.yaml`, Fehler bei falschen Parametern kommen nicht immer auf Top-Level:
schema_version 1 ```json
- `src/mcp_familywall/auth.py`: Keyring-Service `mcp-familywall`, {"a00": {"un": {"un": {"message": "missing value in: taskId"}}}}
Keys: `email`, `password`. Credential-Resolution:
1. Umgebungsvariablen `FW_EMAIL`, `FW_PASSWORD`
2. OS Keyring
- `src/mcp_familywall/fw_client.py`: HTTP-Client mit `httpx`.
Methoden: `login()`, `logout()`, `call(endpoint, params)`.
Debug-Logging wenn `FW_DEBUG=1` (Passwort maskieren).
- `src/mcp_familywall/cli.py`:
- `setup`: fragt E-Mail + Passwort, führt Login/Logout durch,
speichert Credentials im Keyring, gibt Claude-Desktop-Snippet aus
- `check`: testet Auth und API-Erreichbarkeit
- `serve`: startet MCP-Server
Config-Datei enthält nur:
```yaml
schema_version: 1
``` ```
→ `fw_client` muss beide Ebenen prüfen
### Gruppe 2 MCP Tools ✦ Prio: hoch ### Bekannte Parameter-Namen (verifiziert)
| Endpoint | Parameter | Wert/Format |
|---|---|---|
| `taskcreate2` | `taskListId`, `text`, `description`, `taskCategoryId`, `dueDate`, `assignee` | |
| `taskupdate2` | `metaId`, `text`, `description`, `taskCategoryId`, `dueDate`, `assignee`, `taskListId` | |
| `taskupdate2` | `dueDate` löschen | `$empty` |
| `taskmark` | `taskId`, `complete` | `"true"`/`"false"` |
| `metadelete` | `id` | metaId des Tasks |
| `wallmood` | `wall_message_id`, `moodType` | `"STAR"` für Like |
| `taskcategoryput` | `name`, `emoji` | |
| `taskcategorydelete` | `id` | metaId der Kategorie |
`src/mcp_familywall/server.py` + `src/mcp_familywall/modules/lists.py` ### Self-Like-Restriction
Eigene Posts können nicht geliked werden. API antwortet 200, macht aber nichts.
**`get_circles`** ### Unlike nicht möglich
- Ruft `famlistfamily` auf Service Worker verschlüsselt den Unlike-Request-Body.
- Gibt alle Kreise zurück: `id`, `name` Endpoint unbekannt. `like_post(like=False)` gibt Fehlermeldung zurück.
- Confirmation: nein
**`get_lists`**
- Signatur: `get_lists(scope: str = None) -> str`
- Ruft `accgetallfamily` auf (Parameter: `a01call=taskcategorysync`, `a02call=tasksync`)
- Gibt zurück: `id` (metaId), `name` (übersetzt), `type`, offene Einträge,
Gesamteinträge, Kreis-Name
- Ohne `scope`: alle Kreise; mit `scope`: nur dieser Kreis
- Confirmation: nein
**`get_tasks`**
- Signatur: `get_tasks(list_id: str, only_open: bool = True) -> str`
- `list_id`: metaId aus `get_lists`
- Gibt zurück: `id`, `text`, `description`, `completed`
- Confirmation: nein
## Test-Credentials (nur für Entwicklung) ## Test-Credentials (nur für Entwicklung)
@@ -143,9 +137,12 @@ schema_version: 1
| E-Mail | `marcus@gecheckt.de` | | E-Mail | `marcus@gecheckt.de` |
| Passwort | `Lasdas1234` | | Passwort | `Lasdas1234` |
Hinweis: Das ist ein kostenloser Test-Account ohne Premium-Features.
Der echte Account (Premium) hat andere Credentials die im Keyring gespeichert sind.
## Hintergrund ## Hintergrund
Marcus ist Senior Software Engineer (Java, Jakarta EE). Marcus ist Senior Software Engineer (Java, Jakarta EE).
Präferenz: State-of-the-Art, Best Practices, saubere Architektur. Präferenz: State-of-the-Art, Best Practices, saubere Architektur.
Automatisierung spart Zeit für die Familie. 🌱 Automatisierung spart Zeit für die Familie. 🌱
+183 -410
View File
@@ -1,6 +1,6 @@
# Family Wall API Spezifikation # Family Wall API Spezifikation
Erarbeitet durch Browser-Traffic-Analyse (April 2026). Erarbeitet durch Browser-Traffic-Analyse und React-Fiber-Analyse (April 2026).
Es gibt keine offizielle API-Dokumentation. Es gibt keine offizielle API-Dokumentation.
## Base URL ## Base URL
@@ -18,21 +18,14 @@ Content-Type: application/x-www-form-urlencoded
|---|---| |---|---|
| `identifier` | E-Mail-Adresse | | `identifier` | E-Mail-Adresse |
| `password` | Passwort | | `password` | Passwort |
| `type` | nicht senden — wird als `undefined` ignoriert (verifiziert per JS-Analyse) | | `type` | `"email"` (verifiziert) |
| `clientId` | weglassen |
| `clientSecret` | weglassen |
| `generateAutologinToken` | weglassen |
| `countryCode` | weglassen |
**Response (Erfolg):** **Response (Erfolg):**
```json ```json
{ "a00": { "r": { "r": <SessionObject> }, "cn": "log2in" } } { "r": { "r": <SessionObject> } }
``` ```
`SessionObject` enthält u.a. `tokenCsrf` und `webApiUrl`.
`tokenCsrf` ist die Session-ID identisch zur `JSESSIONID` im Cookie.
**Response (Fehler):** **Response (Fehler):**
```json ```json
@@ -41,12 +34,10 @@ Content-Type: application/x-www-form-urlencoded
``` ```
Der Server setzt nach erfolgreichem Login ein Session-Cookie: Der Server setzt nach erfolgreichem Login ein Session-Cookie:
Set-Cookie: JSESSIONID=<session-id> (= tokenCsrf) `Set-Cookie: JSESSIONID=<session-id>`
### Folgecalls (nach Login) ### Folgecalls (nach Login)
Alle API-Calls nach dem Login benötigen:
| | | | | |
|---|---| |---|---|
| **Cookie** | `JSESSIONID=<session-id>` | | **Cookie** | `JSESSIONID=<session-id>` |
@@ -55,7 +46,6 @@ Alle API-Calls nach dem Login benötigen:
### Logout ### Logout
POST https://api.familywall.com/api/log2out POST https://api.familywall.com/api/log2out
Content-Type: application/x-www-form-urlencoded
Keine Parameter. Session wird serverseitig invalidiert. Keine Parameter. Session wird serverseitig invalidiert.
@@ -63,62 +53,66 @@ Keine Parameter. Session wird serverseitig invalidiert.
Kein Session-Caching. Jeder MCP-Tool-Call führt folgende Sequenz aus: Kein Session-Caching. Jeder MCP-Tool-Call führt folgende Sequenz aus:
```
POST /api/log2in → Session-ID POST /api/log2in → Session-ID
POST /api/<endpoint> → Nutzdaten POST /api/<endpoint> → Nutzdaten (ggf. mehrere Calls in einer Session)
POST /api/log2out → Session invalidieren POST /api/log2out → Session invalidieren
```
Credentials (E-Mail + Passwort) werden einmalig via `mcp-familywall setup` Credentials (E-Mail + Passwort) werden einmalig via `mcp-familywall setup`
im OS Keyring gespeichert (Keys: `email`, `password`). Kein Keyring-Eintrag im OS Keyring gespeichert (Keys: `email`, `password`).
für `session_id`.
## Fehlerbehandlung
### Silent-Fail Warnung
Die API gibt Fehler manchmal NICHT auf Top-Level zurück, sondern eingebettet:
```json
{"a00": {"un": {"un": {"message": "missing value in: taskId"}}}}
```
`fw_client.py` prüft beide Ebenen (`a00.un.un` und Top-Level `ex`/`un`)
und wirft `FamilyWallError`. Nie eine Response als Erfolg werten ohne
beide Fehlerebenen zu prüfen.
### Sentinel-Wert `$empty`
Das FiZ-Server-Framework nutzt `$empty` als Sentinel-String um optionale
Felder zu löschen. Alle anderen Werte (`""`, `null`, `"null"`, `"0"`,
`"-1"`, `"remove"`, `"clear"`, Epoch-Timestamps) werden vom Server mit
`"is not a valid Date"` abgelehnt.
**Aktuell genutzt für:**
- `dueDate=$empty` → löscht Fälligkeitsdatum in `taskupdate2`
**Gefunden durch:** React-Fiber-Analyse des nativen `datetime-local`-Inputs
nach Klick auf "Löschen" im Custom-Datepicker wird der Input-Wert auf
`"$empty"` gesetzt, was beim Speichern an die API gesendet wird.
## Bekannte Endpoints ## Bekannte Endpoints
### `famlistfamily` Kreise abrufen ### `famlistfamily` Kreise + Mitglieder abrufen
POST https://api.familywall.com/api/famlistfamily POST https://api.familywall.com/api/famlistfamily
Content-Type: application/x-www-form-urlencoded
**Body-Parameter:** keine (verifiziert)
**Response-Struktur (verifiziert):**
```
a00.r.r[] → Kreise
.metaId → eindeutige Kreis-ID (Format family/<id>)
.name → Kreisname
.family_id → numerische Kreis-ID
.members[] → Mitglieder des Kreises
.accountId → numerische Account-ID
.metaId → Mitglieds-ID (Format familymember/<accountId>_<familyId>)
.firstName → Vorname (Display-Name; bevorzugen gegenüber .name)
.name → E-Mail-Adresse (Family Wall Default wenn kein Anzeigename)
.role → Familienrolle (z.B. "Unknown", "Parent", "Child")
.right → Berechtigung (z.B. "SuperAdmin", "Admin", "Member")
.color → Profilfarbe als Hex-String (z.B. "#FF8086")
.medias[0].pictureUrl → Avatar-URL (generierter Default wenn pictureDefault=true)
.identifiers[] → Kontaktdaten
.type → Typ (z.B. "Email")
.value → Wert (z.B. E-Mail-Adresse)
.familyId → Zugehöriger Kreis (= metaId des Kreises)
.isloggedaccount → "true" wenn das der angemeldete Account ist
.joinDate → Beitrittsdatum (ISO 8601)
.lastLoginDate → Letzter Login (ISO 8601)
.locale → Spracheinstellung (z.B. "de_DE")
.timeZone → Zeitzone (z.B. "Europe/Berlin")
.invitations[] → Offene Einladungen (leer wenn keine)
.coverUri → Cover-Bild URL
```
### `taskgettasklists` Listen abrufen
POST https://api.familywall.com/api/taskgettasklists
Content-Type: application/x-www-form-urlencoded
**Body-Parameter:** keine **Body-Parameter:** keine
**Response-Struktur:** zu verifizieren beim ersten echten Call **Response-Struktur:**
```
a00.r.r[] → Kreise
.metaId → Kreis-ID (z.B. "family/23431854")
.name → Kreis-Name
.members[] → Mitglieder des Kreises
.accountId → numerische User-ID
.firstName → Anzeigename (bevorzugen)
.name → E-Mail als Fallback
.role → Familienrolle (Parent/Child/Unknown)
.right → Berechtigung (SuperAdmin etc.)
.color → Profilfarbe (#RRGGBB)
.medias[0].pictureUrl → Avatar-URL
.identifiers[type=Email].value → E-Mail-Adresse
```
### `accgetallfamily` Listen + Tasks abrufen ### `accgetallfamily` Listen, Tasks, Kategorien abrufen
POST https://api.familywall.com/api/accgetallfamily POST https://api.familywall.com/api/accgetallfamily
Content-Type: application/x-www-form-urlencoded
**Body-Parameter:** **Body-Parameter:**
@@ -127,425 +121,204 @@ Content-Type: application/x-www-form-urlencoded
| `a01call` | `"taskcategorysync"` | | `a01call` | `"taskcategorysync"` |
| `a02call` | `"tasksync"` | | `a02call` | `"tasksync"` |
Hinweis: `a03call=tasklistsync` ist **kein gültiger Endpoint** — API antwortet mit **Response-Struktur:**
"The call tasklistsync is not registered". Nicht verwenden.
`partnerScope`, `a03id`, `withStateBean` werden weggelassen.
**Response-Struktur (verifiziert):**
``` ```
a00 famlistfamily-Daten (Kreise inkl. members[]) Nebeneffekt a00.r.r[]Kategorien (taskcategorysync)
a01.r.r.updatedCreated[] → taskcategorysync (Kategorien/Abteilungen pro Liste) .metaId → Kategorie-ID
.metaId → Kategorie-ID (Format taskCategory/<familyId>_<sysId>) .name → Kategoriename (Systembezeichnung)
.name → Kategoriename (sprachabhängig, z.B. "Beverages") .taskListType → SHOPPING_LIST oder TODOS
.emoji → Emoji-Symbol der Kategorie .sortingIndexByTaskList → Sortierreihenfolge pro Liste
.systemCategoryId → numerische System-ID (sprach-unabhängig) .rights.canDelete → "true" = custom, null = System
.taskListType Listentyp der Kategorie (z.B. "SHOPPING_LIST") .localeSprachcode (de/en/fr/...), fehlt bei custom
**Wichtig:** Alle 171 Kategorien sind SHOPPING_LIST —
es gibt keine TODO-Kategorien in der API a02.r.r.updatedCreated[] → Tasks (tasksync)
.sortingIndexByTaskList → dict: Listen-ID → Sortierposition .metaId → Task-ID
**Achtung:** enthält ALLE Listen-IDs unabhängig vom Typ .text → Aufgabentext
→ NICHT für Typ-Filterung verwenden! .description → optionale Beschreibung
Stattdessen: taskListType der Kategorie mit .taskListId → Listen-ID
taskListType der Liste (aus taskgettasklists) vergleichen .complete → "true" / "false" (String!)
.locale → Sprache des Namens (z.B. "de", "en", "ru", "fr", "es", .taskCategoryId → Kategorie-ID (optional)
"it", "nl", "pt", "sv", "ko", "ja") .dueDate → Fälligkeitsdatum (ISO 8601, optional)
Jede Sprache = eigener Eintrag mit eigenem metaId/systemCategoryId .assignee[] → Liste von Member-IDs (optional)
**Wichtig:** Custom-Kategorien (rights.canDelete='true') haben .assigneeIds[] → alternativ zu assignee[]
kein locale-Feld gesetzt — sie werden sprachunabhängig
zurückgegeben und dürfen nie über locale gefiltert werden.
.hiddenByTaskList → Liste von Listen-IDs, in denen die Kat. versteckt ist
a02.r.r.updatedCreated[] → tasksync (Tasks)
.metaId → eindeutige Task-ID
.taskId → identisch zu metaId (zweiter Alias)
.text → Aufgabentext
.description → optionale Beschreibung
.taskListId → Zugehörigkeit zur Liste
.complete → "true" / "false" (String, nicht Boolean!)
.categories[] → Listen-Level-Systemkategorie (z.B. SYS-CAT-SHOPPINGLIST);
NICHT die spezifische Task-Kategorie — immer identisch
für alle Tasks einer Liste
.system → "true" (immer System-Kategorie)
.name → Listen-Systemkategorien (z.B. "SYS-CAT-SHOPPINGLIST", "SYS-CAT-TODOS")
.taskCategoryId → spezifische Task-Kategorie (verifiziert): metaId-Format
(z.B. "taskCategory/23431854_200"), null wenn nicht gesetzt
.assignee[] → zugewiesene Mitglieder: [{accountId: "..."}]
.assigneeIds[] → zugewiesene Member-IDs als String-Array (z.B. ["23431898"])
.dueDate → Fälligkeitsdatum (ISO 8601, z.B. "2026-04-30T18:00:00.000Z"; null wenn nicht gesetzt)
.reminder → Erinnerungsregel (Objekt mit reminderUnit, reminderType, reminderValue — nicht das Fälligkeitsdatum!)
.recurrency → Wiederholungsregel (optional)
.sortingIndex → Anzeigereihenfolge
``` ```
**Kategorie-Zuweisung bei taskcreate2 / taskupdate2 (verifiziert):** ### `wallget` Wall-Aktivitäten abrufen
| Parameter | Pflicht | Wert |
|---|---|---|
| `taskCategoryId` | nein | Kategorie-MetaId aus `get_categories` (z.B. `taskCategory/23431854_200`) |
Hinweise:
- Wert muss das vollständige metaId-Format `taskCategory/<familyId>_<systemCategoryId>` sein.
Nur der numerische `systemCategoryId`-Teil (z.B. `200`) führt zu API-Fehler
`"cannot find task category id=200"`.
- Das `categories[]`-Feld in der Response zeigt immer `SYS-CAT-SHOPPINGLIST`
(Listen-Level-Systemkategorie, unabhängig vom gesetzten `taskCategoryId`).
Die tatsächliche Task-Kategorie ist im Feld `taskCategoryId` der Task gespeichert.
- Nur für Einkaufslisten (`taskListType=SHOPPING_LIST`) relevant;
TODO-Listen haben keine Kategorien.
## Systembezeichnungen für Listen-Namen
Bekannte Systembezeichnungen werden deutsch übersetzt:
| Systembezeichnung | Deutsch |
|---|---|
| `SYS-CAT-SHOPPINGLIST` | `Einkaufsliste` |
Unbekannte Bezeichnungen werden unverändert zurückgegeben.
Mapping-Tabelle bei Bedarf erweitern.
### `wallget` Aktivitäten (Wall) abrufen
POST https://api.familywall.com/api/wallget POST https://api.familywall.com/api/wallget
Content-Type: application/x-www-form-urlencoded
**Body-Parameter:** **Response-Struktur:**
| Parameter | Wert |
|---|---|
| `nb` | Anzahl Einträge (z.B. `"20"`) |
| `date` | optional, für Paginierung (Datum des letzten Eintrags) |
| `accountId` | optional |
| `type` | optional |
| `nested` | optional |
| `masterNested` | optional |
| `sortBy` | optional |
**Response-Struktur (verifiziert):**
``` ```
a00.r.r[] a00.r.r[]
.metaId → ID der Aktivität (= wallMessageId) .metaId → Post-ID (z.B. "wall/23431854_31119189")
.refType → Aktivitätstyp (z.B. STATUS, FAMILY_CREATED) .type STATUS, FAMILY_CREATED, etc.
.text → Text (optional, fehlt bei System-Events) .text → Post-Text
.creationDate → Datum (ISO 8601) .modifDate → Timestamp (ISO 8601)
.accountId → Autor-ID .creator.accountId → Author-ID
.moodMap → {"<accountId>": ["STAR"]} für Likes
.moodStarShortcut → true wenn geliked
.comments[] → Kommentare
``` ```
**Response-Struktur:** zu verifizieren beim ersten echten Call
### `wallactivityget` Einzelne Aktivität abrufen
POST https://api.familywall.com/api/wallactivityget
Content-Type: application/x-www-form-urlencoded
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `accountId` | optional |
| `masterNested` | optional |
**Response-Struktur:** zu verifizieren beim ersten echten Call
(Endpoint noch nicht implementiert — für spätere Versionen)
## Fehlerstruktur-Varianten
Die Family Wall API gibt Fehler in zwei verschiedenen Strukturen zurück:
| Struktur | Beispiel | Erkennungsregel |
|---|---|---|
| Top-Level `ex` | `{"ex": {"ex": {...}}}` | `"ex" in body` |
| Top-Level `un` | `{"un": {"un": {...}}}` | `"un" in body` |
| Verschachtelt `a00.un` | `{"a00": {"un": {"un": {...}}}}` | `"un" in body["a00"]` |
Ab v0.4.15 erkennt `fw_client.py` alle drei Varianten und wirft `FamilyWallError`.
Zuvor wurden `a00.un`-Fehler still ignoriert (silent fail), was zu irreführenden
`{"updated": True}`-Antworten bei fehlgeschlagenen Updates führte.
**Bekannte Endpoints mit `a00.un`-Fehlern:**
- `taskupdate2` mit ungültigem `dueDate`-Wert
- `taskcategorydelete` mit falschem Parameter-Namen (obsolet nach v0.4.x-Fix)
## Debug-Logging
Wenn die Umgebungsvariable `FW_DEBUG=1` gesetzt ist, loggt `fw_client.py`
vollständige Request-Bodies und Responses nach stderr. Dient zur Verifikation
offener Punkte (z.B. `type`-Parameter beim Login, Kreis-Felder in Response).
**Wichtig:** Keine Secrets in Debug-Ausgaben (Passwort maskieren).
### `taskcreate2` Task erstellen ### `taskcreate2` Task erstellen
POST https://api.familywall.com/api/taskcreate2 POST https://api.familywall.com/api/taskcreate2
Content-Type: application/x-www-form-urlencoded
**Body-Parameter (verifiziert):** **Body-Parameter:**
| Parameter | Pflicht | Wert | | Parameter | Pflicht | Wert |
|---|---|---| |---|---|---|
| `taskListId` | ja | Listen-ID aus `get_lists` (z.B. `taskList/123_456`) | | `taskListId` | ja | Listen-metaId |
| `text` | ja | Aufgabentitel | | `text` | ja | Aufgabentext |
| `description` | nein | Optionale Beschreibung | | `description` | nein | Beschreibung |
| `taskCategoryId` | nein | Kategorie-MetaId aus `get_categories` (z.B. `taskCategory/23431854_200`) | | `taskCategoryId` | nein | Kategorie-metaId (vollständig, z.B. `taskCategory/23431854_200`) |
| `dueDate` | nein | Fälligkeitsdatum ISO 8601 (z.B. `"2026-04-30T18:00:00"`); gespeichert als `dueDate` im Task-Objekt | | `dueDate` | nein | ISO 8601 (z.B. `2026-04-30T18:00:00`) |
| `assignee` | nein | Member-ID aus `get_members` (z.B. `"23431898"`); für mehrere Zuweisungen: mehrfach senden (`assignee=id1&assignee=id2`); gespeichert als `assigneeIds[]` im Task-Objekt | | `assignee` | nein | Member-accountId, mehrfach sendbar für mehrere Zuweisungen |
**Response-Struktur (verifiziert):** **Response:**
``` ```
a00.r.r → vollständiges Task-Objekt der neu erstellten Task a00.r.r → vollständiges Task-Objekt
.metaId → eindeutige Task-ID (z.B. "task/23431854_726362809") .metaId → neue Task-ID
.taskListId → Listen-ID
.text → Titel
.complete → "false" (immer, direkt nach Erstellung)
``` ```
### `taskupdate2` Task aktualisieren ### `taskupdate2` Task aktualisieren
POST https://api.familywall.com/api/taskupdate2 POST https://api.familywall.com/api/taskupdate2
Content-Type: application/x-www-form-urlencoded
**Body-Parameter (verifiziert):** **Body-Parameter:**
| Parameter | Pflicht | Wert | | Parameter | Pflicht | Wert |
|---|---|---| |---|---|---|
| `metaId` | ja | Task-ID aus `get_tasks` | | `metaId` | ja | Task-metaId |
| `text` | nein | Neuer Titel (mindestens eines der optionalen Felder erforderlich) | | `text` | nein | neuer Titel |
| `description` | nein | Neue Beschreibung | | `description` | nein | neue Beschreibung |
| `taskCategoryId` | nein | Kategorie-MetaId aus `get_categories` (z.B. `taskCategory/23431854_200`) | | `taskCategoryId` | nein | neue Kategorie-metaId |
| `dueDate` | nein | Fälligkeitsdatum ISO 8601 (z.B. `"2026-04-30T18:00:00"`); gespeichert als `dueDate` im Task-Objekt | | `dueDate` | nein | ISO 8601 oder `$empty` zum Löschen |
| `assignee` | nein | Member-ID aus `get_members` (z.B. `"23431898"`); für mehrere Zuweisungen: mehrfach senden (`assignee=id1&assignee=id2`); leerer String (`""`) entfernt alle Zuweisungen (nicht verifiziert); gespeichert als `assigneeIds[]` im Task-Objekt | | `assignee` | nein | Member-accountId (mehrfach sendbar), `""` zum Entfernen aller |
| `taskListId` | nein | Ziel-Listen-ID zum Verschieben des Tasks (verifiziert ändert `taskListId` im Task-Objekt) | | `taskListId` | nein | neue Listen-metaId (verschiebt Task) |
Hinweis: `taskListId` ist optional ohne diesen Parameter bleibt der Task in seiner aktuellen Liste. **Hinweis:** `taskListId` ist NICHT Pflicht beim Update.
**Response-Struktur:** kein spezifischer Rückgabewert Erfolg = kein `ex`/`un`-Key auf Top-Level.
**dueDate Clearing `"$empty"` Sentinel (verifiziert April 2026, v0.4.16):**
Der FiZ-Framework verwendet den Sentinel-Wert `"$empty"` um optionale Felder zu leeren.
**Response:**
``` ```
POST taskupdate2 a00.r.r → vollständiges Task-Objekt
metaId=task/23431854_726333919&dueDate=$empty
→ dueDate wird auf null gesetzt ✓
``` ```
`update_task(clear_due_date=True)` sendet intern `dueDate=$empty`. ### `taskmark` Task abhaken/wiedereröffnen
**Getestete Werte die fehlschlugen (vor der Lösung):**
| Wert | API-Response |
|---|---|
| `""` (leerer String) | `a00.un: " is not a valid Date"` |
| `"null"` | `a00.un: "Cannot parse date val=null"` |
| `"undefined"` | `a00.un: "Cannot parse date val=undefined"` |
| `"0"`, `"-1"` | `a00.un: "... is not a valid Date"` |
| `"remove"` / `"clear"` | `a00.un: "Cannot parse date val=..."` |
| `"1970-01-01T00:00:00"` | Erfolgreich setzt dueDate auf Unix-Epoch (kein Clearing!) |
| Feld weglassen | dueDate bleibt unverändert |
| `removeDueDate=1`, `clearDueDate=1` | werden ignoriert |
**Wichtig:** Die Fehler werden als `a00.un` zurückgegeben (nicht Top-Level `un`).
Ab v0.4.15 erkennt `fw_client` diese und wirft `FamilyWallError`.
### `taskmark` Task als erledigt/offen markieren
POST https://api.familywall.com/api/taskmark POST https://api.familywall.com/api/taskmark
Content-Type: application/x-www-form-urlencoded
**Body-Parameter (verifiziert):** **Body-Parameter:**
| Parameter | Pflicht | Wert | | Parameter | Wert |
|---|---|---| |---|---|
| `taskId` | ja | Task-ID aus `get_tasks` (**WICHTIG: `taskId`, nicht `metaId`!**) | | `taskId` | Task-metaId ⚠️ nicht `metaId`! |
| `complete` | ja | `"true"` oder `"false"` (String, nicht Boolean!) | | `complete` | `"true"` oder `"false"` (String!) |
**Achtung:** Der Endpoint heißt intern `taskId`, nicht `metaId`. **Response:**
Falsche Parameter (`metaId`, `id`, `taskMetaId`) werden serverseitig ignoriert
die API antwortet dann mit einem Fehler in `a00.un.un` (nicht Top-Level!),
der vom Standard-Error-Check im fw_client übersehen wird.
**Response-Struktur (verifiziert):**
``` ```
a00.r.r → vollständiges Task-Objekt mit aktuellem Stand (inkl. lastAction: "MARK_COMPLETED") a00.r.r → Task-Objekt mit lastAction: "MARK_COMPLETED"
``` ```
### `metadelete` Objekt löschen ### `metadelete` Task löschen
POST https://api.familywall.com/api/metadelete POST https://api.familywall.com/api/metadelete
Content-Type: application/x-www-form-urlencoded
**Body-Parameter (verifiziert):** **Body-Parameter:**
| Parameter | Pflicht | Wert | | Parameter | Wert |
|---|---|---| |---|---|
| `id` | ja | Task-ID aus `get_tasks` (**WICHTIG: `id`, nicht `metaId`!**) | | `id` | Task-metaId ⚠️ nicht `metaId`! |
Hinweis: `metadelete` ist ein generischer Lösch-Endpoint für beliebige Objekte (Tasks, etc.). **Response:**
Entsprechend vorsichtig verwenden.
**Response-Struktur (verifiziert):**
``` ```
a00.r.r → "true" (String) a00.r.r → "true" (String)
``` ```
**Fehlerverhalten:** Bei falschem Parameter-Namen (`metaId`, `taskId` etc.) antwortet die API ### `wallmood` Post liken
mit `{"a00": {"un": {"un": {"message": "missing value in: id"}}}}` auf Top-Level ohne `un`-Key POST https://api.familywall.com/api/wallmood
→ wird vom fw_client fälschlich als Erfolg interpretiert. Daher ist der korrekte Parameter-Name
kritisch.
### `taskcategoryput` Kategorie erstellen **Body-Parameter:**
| Parameter | Wert |
|---|---|
| `wall_message_id` | Post-metaId ⚠️ nicht `wallId` oder `id`! |
| `moodType` | `"STAR"` für Like |
**Bekannte Einschränkungen:**
- Unlike: Endpoint/Parameter unbekannt (Service Worker verschlüsselt Request-Body)
- Self-Like: API antwortet 200, macht aber serverseitig nichts
- `moodType="NONE"` und andere Werte haben keine Wirkung
**Response:**
```
a00.r.r → Wall-Objekt mit moodMap, refAction: "MOOD_STAR"
```
### `taskcategoryput` Kategorie erstellen/aktualisieren
POST https://api.familywall.com/api/taskcategoryput POST https://api.familywall.com/api/taskcategoryput
Content-Type: application/x-www-form-urlencoded
**Body-Parameter (verifiziert via FW_DEBUG=1):** **Body-Parameter:**
| Parameter | Pflicht | Wert | | Parameter | Pflicht | Wert |
|---|---|---| |---|---|---|
| `name` | ja | Kategorie-Name (beliebiger String) | | `name` | ja | Kategoriename |
| `emoji` | nein | Icon: Unicode-Emoji-Zeichen (z.B. `🌿`) oder beliebiger String-Code (z.B. `"FOOD"`) — wird as-is gespeichert | | `emoji` | nein | Unicode-Emoji oder beliebiger String |
Hinweise: **Hinweis:** Kategorien sind family-wide `listId` hat keine Wirkung.
- Die neue Kategorie wird **allen** Listen der Familie zugeordnet — es gibt keine per-Liste-Einschränkung.
- Benutzerdefinierte Kategorien haben `systemCategoryId=null` und `rights.canDelete='true'`.
- System-Kategorien haben `rights.canDelete=null` — API erlaubt Löschen, aber `delete_category` Tool verweigert es.
**Response-Struktur (verifiziert):** **Response:**
``` ```
a00.r.r → vollständiges Kategorie-Objekt a00.r.r Kategorie-Objekt mit metaId
.metaId → neue Kategorie-ID (z.B. "taskCategory/23431854_4956637")
.name → Kategorie-Name
.taskListType → "SHOPPING_LIST" (automatisch gesetzt)
.familyId → Familien-ID
.accountId → Account-ID des Erstellers
.rights.canDelete → "true" (custom Kategorien)
.rights.canUpdate → "true" (custom Kategorien)
.emoji → gespeicherter Icon-Wert (falls übergeben)
```
**Fehlerverhalten:** Ohne `name`-Parameter:
```json
{"a00": {"un": {"un": {"FiZClassId": "502", "message": "cat without a name ..."}}}}
``` ```
### `taskcategorydelete` Kategorie löschen ### `taskcategorydelete` Kategorie löschen
POST https://api.familywall.com/api/taskcategorydelete POST https://api.familywall.com/api/taskcategorydelete
Content-Type: application/x-www-form-urlencoded
**Body-Parameter (verifiziert via FW_DEBUG=1):** **Body-Parameter:**
| Parameter | Pflicht | Wert | | Parameter | Wert |
|---|---|---|
| `id` | ja | Kategorie-MetaId aus `get_categories` (**WICHTIG: `id`, nicht `metaId`!**) |
**Achtung:** Falscher Parameter-Name `metaId` führt zu:
```json
{"a00": {"un": {"un": {"FiZClassId": "502", "message": "In request, missing value in : id"}}}}
```
**Response-Struktur (verifiziert):**
```
a00.r.r → "true" (String)
```
**Wichtig System-Kategorien:** Die API erlaubt technisch das Löschen von System-Kategorien
(`taskCategory/<familyId>_200` etc.), entfernt sie aber nur aus der Familie — nicht global.
Das `delete_category`-MCP-Tool verweigert dies (Schutz via `rights.canDelete`-Check).
Erkennung: custom Kategorien haben `rights.canDelete='true'`; System-Kategorien haben `rights.canDelete=null`.
### `wallmood` Wall-Post liken
POST https://api.familywall.com/api/wallmood
Content-Type: application/x-www-form-urlencoded
**Body-Parameter (verifiziert via FW_DEBUG=1):**
| Parameter | Pflicht | Wert |
|---|---|---|
| `wall_message_id` | ja | Post-ID aus `get_activities` (z.B. `wall/23431854_31119189`) |
| `moodType` | ja | `"STAR"` (einziger bekannter Wert — entspricht dem Like-Button in der App) |
**Verhalten (verifiziert):**
- `wallmood` mit `moodType: "STAR"` ist eine **idempotente SET-Operation** — kein Toggle!
- Mehrfaches Aufrufen mit denselben Parametern hinterlässt denselben Zustand
- `moodType: "LIKE"` wurde serverseitig als `"STAR"` gespeichert → korrekter Wert ist `"STAR"`
- Nur für Post-Typ `STATUS` wirksam; `FAMILY_CREATED` und vermutlich System-Posts ignorieren den Call
**Unlike nicht implementierbar (Stand: April 2026):**
Ausgiebig getestete Ansätze, die alle fehlschlugen:
| Ansatz | Ergebnis |
|---|---| |---|---|
| `moodType: "NONE"` / `"REMOVE"` / `"DELETE"` / `""` | moodMap unverändert | | `id` | Kategorie-metaId ⚠️ nicht `metaId`! |
| `moodType` ganz weglassen | moodMap unverändert |
| `moodStarShortcut: "false"` als Parameter | moodMap unverändert |
| Alternative Endpoints: `wallmooddelete`, `wallmoodremove`, `wallmoodelete`, `wallunmood`, `wallcommentdelete`, `wallmoodstar`, `wallstar`, `wallreact`, `wallreactdelete` | alle: `"The call X is not registered"` (502) |
| `metadelete` auf Mood-Comment-ID | löscht den Comment, aber `moodStarShortcut` bleibt gesetzt |
Die Web-App nutzt einen Service Worker, der Requests abfängt — der echte Unlike-Payload **Hinweis:** System-Kategorien (`rights.canDelete=null`) können technisch
ist dadurch nicht per Browser-DevTools inspizierbar. Unlike bleibt bis zur weiteren gelöscht werden, sind dann aber dauerhaft weg und nicht wiederherstellbar.
Analyse nicht unterstützt. MCP-Server schützt dagegen durch Check auf `rights.canDelete`.
**Response-Struktur (verifiziert):** ### `taskgettasklists` Listen abrufen (alternativ)
``` POST https://api.familywall.com/api/taskgettasklists
a00.r.r → vollständiges Wall-Message-Objekt
.metaId → Post-ID (= wallMessageId)
.wallMessageId → Post-ID (identisch zu metaId)
.refType → Aktivitätstyp (z.B. "STATUS")
.refAction → Letzte Aktion (z.B. "MOOD_STAR")
.text → Post-Text
.postAccountId → Account-ID des Post-Autors
.accountId → Account-ID (identisch)
.moodMap → dict: accountId → ["STAR"] wenn geliked
.moodStarShortcut → "true" wenn Like-Shortcut aktiv
.comments[] → Liste von Kommentaren/Moods
.commentId → Kommentar-ID (Format wallComment/...)
.accountId → Account-ID des Kommentators
.mood → Mood-Typ (z.B. "STAR")
.text → Kommentartext (falls vorhanden)
.creationDate → Erstelldatum (ISO 8601)
.creationDate → Erstelldatum des Posts (ISO 8601)
.modifDate → Letztes Änderungsdatum (ISO 8601)
.familyId → Kreis-ID (Format family/...)
.rights.canUpdate → "true"/"false"
.rights.canDelete → "true"/"false"
```
**Like-Zustand bestimmen (zwei Indikatoren, beide auswerten):** Wird intern zur Verifikation von `taskListType` genutzt.
| Feld | Typ | Bedeutung | ## Systembezeichnungen für Listen-Namen
|---|---|---|
| `moodStarShortcut` | `"true"` / `"false"` | Primär: direktes User-Like-Flag für den anfragenden Account |
| `moodMap[accountId]` | `["STAR"]` | Sekundär: accountId → Mood-Liste; enthält `"STAR"` wenn geliked |
Beide Indikatoren können den Like-Zustand korrekt abbilden — je nach API-internem | Systembezeichnung | Deutsch |
Speicherpfad ist nur einer gesetzt. Immer beide prüfen. |---|---|
| `SYS-CAT-SHOPPINGLIST` | `Einkaufsliste` |
| `SYS-CAT-TODOLIST` | `Aufgaben` |
**Silent-Fail-Szenarien (API antwortet 200, aber Like wird nicht gesetzt):** ## Debug-Logging
- **Self-Like-Restriction**: Eigener Post kann nicht geliked werden
(verifiziert: Account 23431898 kann Post `wall/23431854_31119189` nicht liken,
obwohl API regulär antwortet — `modifDate` bleibt eingefroren)
- **Unsupported Post-Typ**: `FAMILY_CREATED`-Posts ignorieren wallmood-Calls
- **Rate-Limit**: Nach vielen Calls kann die API Still-Fails zurückgeben
Erkennungsmerkmal für Silent-Fail: `modifDate` im Response identisch zum Vorherigen Wenn die Umgebungsvariable `FW_DEBUG=1` gesetzt ist, loggt `fw_client.py`
AND `moodStarShortcut: false` AND `moodMap: {}`. vollständige Request-Bodies und Responses nach stderr.
## Noch zu verifizieren **Wichtig:** Keine Secrets in Debug-Ausgaben (Passwort maskieren).
- ~~Exakter Wert für `type`-Parameter beim Login~~ → nicht senden (verifiziert per JS-Analyse) **Wann FW_DEBUG=1 nutzen:**
- ~~Response-Struktur von `famlistfamily` (Kreise)~~ → a00.r.r[], metaId + name (verifiziert) - Neue Endpoints verifizieren
- ~~Ob `a03call=tasklistsync` benötigt wird~~ → **nein**, kein gültiger Endpoint (verifiziert) - Parameter-Namen unbekannt
- Listen-IDs aus `a01.r.r.updatedCreated[].sortingIndexByTaskList`-Keys (verifiziert) - Silent-Fail debuggen
- Listen-Namen und Zähler (remainingTaskNumber, totalTaskNumber) → noch unbekannt - Service Worker blockiert Browser-DevTools
- Kreis-Zuordnung in `accgetallfamily`-Response → noch offen
- ~~Ob `partnerScope` / `withStateBean` benötigt werden~~ → nein (verifiziert) ## Service Worker
- Session-Lebensdauer (irrelevant da kein Caching)
- ~~`taskcreate2`: Response-Struktur~~ → `a00.r.r` = vollständiges Task-Objekt (verifiziert) Die Family Wall Web-App registriert einen Service Worker der bestimmte
- ~~`taskupdate2`: ob `taskListId` Pflichtfeld ist~~ → **nein**, nicht erforderlich (verifiziert) HTTP-Requests abfängt und modifiziert bevor sie das Netzwerk erreichen.
- ~~`taskmark`: korrekter Parameter-Name~~ → **`taskId`** (nicht `metaId`!) (verifiziert) Betroffen sind u.a. Unlike-Calls und möglicherweise andere schreibende
- ~~`metadelete`: korrekter Parameter-Name + Response-Struktur~~ → **`id`**, Response `"true"` (verifiziert) Operationen.
- ~~`wallmood`: Parameter-Name `wallId`~~ → **`wall_message_id`** (verifiziert via API-Fehlermeldung)
- ~~`wallmood`: `moodType`-Werte, Toggle vs. explizit, Response-Struktur~~ → verifiziert: idempotentes SET mit `"STAR"`, kein Toggle (siehe oben) **Folge:** Browser-DevTools Network-Tab und JS-Interceptoren (XHR/Fetch)
- `wallmood` Unlike: Mechanismus unbekannt — Service Worker verhindert Browser-Inspektion; alle getesteten Ansätze fehlgeschlagen (siehe oben) zeigen nicht den echten Request-Body für diese Calls.
- ~~`taskcreate2` / `taskupdate2`: Kategorie-Paramter-Name~~ → **`taskCategoryId`**, Wert = vollständige metaId (verifiziert) **Lösung:** `FW_DEBUG=1` auf MCP-Server-Seite zeigt was tatsächlich gesendet wird.
- ~~`taskcategoryput`: Body-Parameter, Response-Struktur~~ → `name` (Pflicht), `emoji` (optional), Response = neues Kategorie-Objekt (verifiziert)
- ~~`taskcategorydelete`: Body-Parameter~~ → **`id`** (nicht `metaId`!), Response = `"true"` (verifiziert) ## Offene Punkte
- ~~`taskcreate2` / `taskupdate2`: Fälligkeitsdatum-Parameter~~ → **`dueDate`**, ISO 8601 String, gespeichert als `dueDate` im Task-Objekt (verifiziert)
- ~~`taskcreate2` / `taskupdate2`: Zuweisung-Parameter~~ → **`assignee`** (Member-ID String; mehrere Werte = mehrfach senden); gespeichert als `assigneeIds[]` (verifiziert) - Unlike-Endpoint (Service Worker blockiert Analyse)
- ~~`taskupdate2`: Task verschieben~~ → **`taskListId`** als optionaler Parameter setzt neue Liste (verifiziert) - Erinnerungen (reminder) nur Premium-Account
- `taskupdate2`: Alle Zuweisungen entfernen (leere `assignee`-Liste) → noch nicht verifiziert - Wiederholungen (repeat) nur Premium-Account
- ~~`taskupdate2`: `dueDate` entfernen (Clearing)~~**`dueDate=$empty`** (FiZ-Sentinel, verifiziert April 2026, v0.4.16) - Sortierung von Kategorien via API