# Family Wall API – Spezifikation Erarbeitet durch Browser-Traffic-Analyse und React-Fiber-Analyse (April 2026). Es gibt keine offizielle API-Dokumentation. ## Base URL https://api.familywall.com/api ## Authentifizierung ### Login POST https://api.familywall.com/api/log2in Content-Type: application/x-www-form-urlencoded **Request-Parameter:** | Parameter | Wert | |---|---| | `identifier` | E-Mail-Adresse | | `password` | Passwort | | `type` | `"email"` (verifiziert) | **Response (Erfolg):** ```json { "r": { "r": } } ``` **Response (Fehler):** ```json { "ex": { "ex": } } { "un": { "un": } } ``` Der Server setzt nach erfolgreichem Login ein Session-Cookie: `Set-Cookie: JSESSIONID=` ### Folgecalls (nach Login) | | | |---|---| | **Cookie** | `JSESSIONID=` | | **Header** | `Tokencsrf: ` (identisch zur JSESSIONID) | | **Content-Type** | `application/x-www-form-urlencoded` | ### Logout POST https://api.familywall.com/api/log2out Keine Parameter. Session wird serverseitig invalidiert. ### Session-Strategie Kein Session-Caching. Jeder MCP-Tool-Call führt folgende Sequenz aus: ``` POST /api/log2in → Session-ID POST /api/ → Nutzdaten (ggf. mehrere Calls in einer Session) POST /api/log2out → Session invalidieren ``` Credentials (E-Mail + Passwort) werden einmalig via `mcp-familywall setup` im OS Keyring gespeichert (Keys: `email`, `password`). ## 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 ### `famlistfamily` – Kreise + Mitglieder abrufen POST https://api.familywall.com/api/famlistfamily **Body-Parameter:** keine **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, Kategorien abrufen POST https://api.familywall.com/api/accgetallfamily **Body-Parameter:** | Parameter | Wert | |---|---| | `a01call` | `"taskcategorysync"` | | `a02call` | `"tasksync"` | **Response-Struktur:** ``` a00.r.r[] → Kategorien (taskcategorysync) .metaId → Kategorie-ID .name → Kategoriename (Systembezeichnung) .taskListType → SHOPPING_LIST oder TODOS .sortingIndexByTaskList → Sortierreihenfolge pro Liste .rights.canDelete → "true" = custom, null = System .locale → Sprachcode (de/en/fr/...), fehlt bei custom a02.r.r.updatedCreated[] → Tasks (tasksync) .metaId → Task-ID .text → Aufgabentext .description → optionale Beschreibung .taskListId → Listen-ID .complete → "true" / "false" (String!) .taskCategoryId → Kategorie-ID (optional) .dueDate → Fälligkeitsdatum (ISO 8601, optional) .assignee[] → Liste von Member-IDs (optional) .assigneeIds[] → alternativ zu assignee[] ``` ### `wallget` – Wall-Aktivitäten abrufen POST https://api.familywall.com/api/wallget **Response-Struktur:** ``` a00.r.r[] .metaId → Post-ID (z.B. "wall/23431854_31119189") .type → STATUS, FAMILY_CREATED, etc. .text → Post-Text .modifDate → Timestamp (ISO 8601) .creator.accountId → Author-ID .moodMap → {"": ["STAR"]} für Likes .moodStarShortcut → true wenn geliked .comments[] → Kommentare ``` ### `taskcreate2` – Task erstellen POST https://api.familywall.com/api/taskcreate2 **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `taskListId` | ja | Listen-metaId | | `text` | ja | Aufgabentext | | `description` | nein | Beschreibung | | `taskCategoryId` | nein | Kategorie-metaId (vollständig, z.B. `taskCategory/23431854_200`) | | `dueDate` | nein | ISO 8601 (z.B. `2026-04-30T18:00:00`) | | `assignee` | nein | Member-accountId, mehrfach sendbar für mehrere Zuweisungen | **Response:** ``` a00.r.r → vollständiges Task-Objekt .metaId → neue Task-ID ``` ### `taskupdate2` – Task aktualisieren POST https://api.familywall.com/api/taskupdate2 **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `metaId` | ja | Task-metaId | | `text` | nein | neuer Titel | | `description` | nein | neue Beschreibung | | `taskCategoryId` | nein | neue Kategorie-metaId | | `dueDate` | nein | ISO 8601 oder `$empty` zum Löschen | | `assignee` | nein | Member-accountId (mehrfach sendbar), `""` zum Entfernen aller | | `taskListId` | nein | neue Listen-metaId (verschiebt Task) | **Hinweis:** `taskListId` ist NICHT Pflicht beim Update. **Response:** ``` a00.r.r → vollständiges Task-Objekt ``` ### `taskmark` – Task abhaken/wiedereröffnen POST https://api.familywall.com/api/taskmark **Body-Parameter:** | Parameter | Wert | |---|---| | `taskId` | Task-metaId ⚠️ nicht `metaId`! | | `complete` | `"true"` oder `"false"` (String!) | **Response:** ``` a00.r.r → Task-Objekt mit lastAction: "MARK_COMPLETED" ``` ### `metadelete` – Task löschen POST https://api.familywall.com/api/metadelete **Body-Parameter:** | Parameter | Wert | |---|---| | `id` | Task-metaId ⚠️ nicht `metaId`! | **Response:** ``` a00.r.r → "true" (String) ``` ### `wallmood` – Post liken POST https://api.familywall.com/api/wallmood **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 **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `name` | ja | Kategoriename | | `emoji` | nein | Unicode-Emoji oder beliebiger String | **Hinweis:** Kategorien sind family-wide – `listId` hat keine Wirkung. **Response:** ``` a00.r.r → Kategorie-Objekt mit metaId ``` ### `taskcategorydelete` – Kategorie löschen POST https://api.familywall.com/api/taskcategorydelete **Body-Parameter:** | Parameter | Wert | |---|---| | `id` | Kategorie-metaId ⚠️ nicht `metaId`! | **Hinweis:** System-Kategorien (`rights.canDelete=null`) können technisch gelöscht werden, sind dann aber dauerhaft weg und nicht wiederherstellbar. MCP-Server schützt dagegen durch Check auf `rights.canDelete`. ### `taskcreatelist` – Liste erstellen POST https://api.familywall.com/api/taskcreatelist **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `name` | ja | Listen-Name (max 200 Zeichen) | | `taskListType` | ja | `"SHOPPING_LIST"` oder `"TODOS"` | | `sharedToAll` | nein | `"true"` / `"false"` (default: `"true"`) | | `color` | nein | Hex-Farbwert z.B. `"#4784EC"` | | `emoji` | nein | Unicode-Emoji z.B. `"🛒"` | | `scope` | nein | Kreis-metaId z.B. `"family/23447378"` (ohne scope → primärer Kreis) | **Scope-Verhalten:** - Ohne `scope`: Liste wird im primären Kreis des Accounts erstellt - Mit `scope=family/XXXX`: Liste wird im angegebenen Kreis erstellt - Die `metaId` der neuen Liste kodiert den Kreis: `taskList/CIRCLENUM_LISTNUM` - Parameter `familyId`, `circleId`, `family`, `id` → werden ignoriert, nur `scope` wirkt **Response:** ``` a00.r.r → vollständiges Listen-Objekt .metaId → neue Listen-ID (z.B. "taskList/23431854_29759623") .name → Listen-Name .taskListType → SHOPPING_LIST oder TODOS .sharedToAll → "true" / "false" .emoji → Unicode-Emoji (fehlt wenn nicht gesetzt) .color → Hex-Farbwert z.B. "#E53935" (fehlt wenn nicht gesetzt) .familyId → Kreis-metaId des erstellten Liste .rights.canDelete → "true" (user-created lists) ``` **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ### `taskdeletelist` – Liste löschen POST https://api.familywall.com/api/taskdeletelist **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `id` | ja | Listen-metaId ⚠️ nicht `listId` oder `taskListId`! | | `scope` | nein | Kreis-metaId (erforderlich für sekundäre Kreise) | **Response:** ``` a00.r.r → "true" (String) ``` **Hinweis:** Löscht die Liste und alle enthaltenen Tasks unwiderruflich. System-Listen (`rights.canDelete` fehlt oder `null`) sind nicht löschbar. MCP-Server prüft dies vor dem Löschen via `taskgettasklists`. Für Listen in sekundären Kreisen muss `scope=family/XXXX` mitgeschickt werden. **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ### `taskgettasklists` – Listen abrufen POST https://api.familywall.com/api/taskgettasklists **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `scope` | nein | Kreis-metaId z.B. `"family/23447378"` (ohne scope → primärer Kreis) | **Scope-Verhalten:** - Ohne `scope`: Nur Listen des primären Kreises werden zurückgegeben - Mit `scope=family/XXXX`: Nur Listen des angegebenen Kreises - Es gibt keinen servereitigen Filter für mehrere Kreise gleichzeitig - Andere Parameter (`familyId`, `circleId`, etc.) werden ignoriert **Response-Struktur:** ``` a00.r.r[] → Liste aller Task-Listen des Kreises .metaId → Listen-ID (z.B. "taskList/23431854_29740942") .name → Systembezeichnung oder Benutzer-Name .taskListType → SHOPPING_LIST oder TODOS .familyId → Kreis-metaId (z.B. "family/23431854") .emoji → Unicode-Emoji oder "" (leerer String = kein Emoji) .color → Hex-Farbwert z.B. "#E53935" (fehlt wenn nicht gesetzt) .remainingTaskNumber → offene Tasks (String) .totalTaskNumber → Gesamt-Tasks (String) .sharedToAll → "true" / "false" .sharedMemberIds[] → Member-accountIds .rights.canDelete → "true" = löschbar, fehlt/leer = Systemliste .systemId → vorhanden nur bei Systemlisten (z.B. "-10", "-11") ``` **metaId-Encoding:** `taskList/_` — die `family_num` kodiert den Kreis. Beispiel: `taskList/23431854_29740942` gehört zu `family/23431854`. **Hinweis emoji/color:** - `emoji`: Systemlisten liefern `""`, user-created Listen liefern den Emoji-String oder `""` wenn kein Emoji gesetzt. Normalisierung: `""` → `null` im MCP-Server. - `color`: Fehlt komplett wenn nicht gesetzt (nicht `null` oder `""`). Normalisierung: fehlendes Feld → `null` im MCP-Server. **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ## Systembezeichnungen für Listen-Namen | Systembezeichnung | Deutsch | |---|---| | `SYS-CAT-SHOPPINGLIST` | `Einkaufsliste` | | `SYS-CAT-TODOLIST` | `Aufgaben` | ## Debug-Logging Wenn die Umgebungsvariable `FW_DEBUG=1` gesetzt ist, loggt `fw_client.py` vollständige Request-Bodies und Responses nach stderr. **Wichtig:** Keine Secrets in Debug-Ausgaben (Passwort maskieren). **Wann FW_DEBUG=1 nutzen:** - Neue Endpoints verifizieren - Parameter-Namen unbekannt - Silent-Fail debuggen - Service Worker blockiert Browser-DevTools ## Service Worker Die Family Wall Web-App registriert einen Service Worker der bestimmte HTTP-Requests abfängt und modifiziert bevor sie das Netzwerk erreichen. Betroffen sind u.a. Unlike-Calls und möglicherweise andere schreibende Operationen. **Folge:** Browser-DevTools Network-Tab und JS-Interceptoren (XHR/Fetch) zeigen nicht den echten Request-Body für diese Calls. **Lösung:** `FW_DEBUG=1` auf MCP-Server-Seite zeigt was tatsächlich gesendet wird. ### `mprecipeput` – Rezept erstellen POST https://api.familywall.com/api/mprecipeput **Wichtig:** Alle Parameter haben das Präfix `recipe.` (verifiziert via FW_DEBUG=1). **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `recipe.name` | ja | Rezeptname | | `recipe.isRecipe` | ja | immer `"true"` | | `recipe.description` | nein | Kurzbeschreibung | | `recipe.ingredients` | nein | Zutaten als Freitext (Zeilen mit `\n` trennen) | | `recipe.instructions` | nein | Anleitung als Freitext (Zeilen mit `\n` trennen) | | `recipe.prepTime` | nein | Zubereitungszeit in Minuten (String, z.B. `"30"`) | | `recipe.cookTime` | nein | Kochzeit in Minuten (String, z.B. `"60"`) | | `recipe.serves` | nein | Portionen (String, z.B. `"4"`) | | `recipe.url` | nein | externe URL | **Response:** ``` a00.r.r → vollständiges Rezept-Objekt .metaId → neue Rezept-ID (z.B. "recipe/23431854_10968866") .name → Rezeptname .description → Beschreibung .ingredients → Zutaten Freitext (API liefert \r\n als Zeilenumbrüche) .ingredientsList[] → auto-geparste Zutaten (read-only, vom Server generiert) .metaId → "recipeIngredient/" .name → Zutatname .instructions → Anleitung Freitext .prepTime → Zubereitungszeit als String ("30") .cookTime → Kochzeit als String ("60") .serves → Portionen als String ("4") .url → externe URL (fehlt wenn leer) .isRecipe → "true" .isFavorite → "false" .recipeCategories[] → [] .recipeCategoryIdList[] → [] .rights.canDelete → "true" für eigene Rezepte .rights.canUpdate → "true" für eigene Rezepte .familyId, .accountId, .creationDate, .moodMap, .moodStarShortcut ``` **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ### `mprecipeput` – Rezept aktualisieren (Update) POST https://api.familywall.com/api/mprecipeput Identisch zum Create-Aufruf, aber mit zusätzlichem `recipe.metaId`-Parameter. Der Server unterscheidet Create vs. Update anhand ob `recipe.metaId` vorhanden ist. Nur geänderte Felder müssen mitgeschickt werden (Partial Update). `recipe.name` und `recipe.isRecipe="true"` sollten immer mitgeschickt werden. **Zusätzlicher Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `recipe.metaId` | ja (für Update) | metaId des zu aktualisierenden Rezepts | **Newline-Hinweis:** Zutaten und Anleitung müssen echte `\n`-Zeichen enthalten (nicht die zwei-Zeichen-Sequenz `\n`). Literale Backslash-n werden vom Server als ein Element interpretiert → kein Splitting in `ingredientsList`. **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ### `metasync` (id='recipe') – Alle Rezepte abrufen POST https://api.familywall.com/api/metasync **Body-Parameter:** | Parameter | Wert | |---|---| | `id` | `"recipe"` (lowercase, enum-Wert) | **Response-Struktur:** ``` a00.r.r.updatedCreated[] → Liste aller Rezepte der Familie → Felder identisch mit mprecipeput-Response (siehe oben) ``` **Hinweis:** Der Parameter `id` nimmt einen MetaIdTypeEnum-Wert, kein tatsächliches Objekt. Nur `"recipe"` (lowercase) funktioniert – `"RECIPE"`, `"Recipe"` und andere Schreibweisen liefern `MetaIdTypeEnum`-Fehler. **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ### `metadelete` – Rezept löschen POST https://api.familywall.com/api/metadelete Identisch mit dem Task-Löschen-Endpoint. Funktioniert auch für Rezepte. **Body-Parameter:** | Parameter | Wert | |---|---| | `id` | Rezept-metaId (z.B. `"recipe/23431854_10968866"`) | **Response:** ``` a00.r.r → "true" (String) ``` **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ### `acccreatefamily` – Kreis erstellen POST https://api.familywall.com/api/acccreatefamily **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `name` | ja | Kreis-Name | **Response:** ``` a00.r.r → numerische Kreis-ID als String (z.B. "23447369") → vollständige metaId: "family/" + id ``` **Hinweise:** - Der Server kapitalisiert den ersten Buchstaben des Namens. - Kreise können über die API nicht gelöscht werden (`metadelete` → "delete not supported"). - Vollständige Kreisdaten (incl. Name) via `famlistfamily` im gleichen Session-Call abrufbar. **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ### `accinvite` – Mitglied einladen POST https://api.familywall.com/api/accinvite **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `familyId` | ja | Kreis-metaId (z.B. `"family/23447369"`) | | `identifier` | ja | E-Mail-Adresse des Eingeladenen | | `role` | ja | `"Unknown"` (einziger verifizierter Enum-Wert für `FamilyRoleTypeEnum`) | | `firstname` | ja | Vorname des Eingeladenen | **Einschränkung:** Der Endpoint funktioniert nur für Personen, die noch kein Family Wall-Konto haben. Bei bestehenden Accounts antwortet der Server mit: `"This api is now only used to create and invite an account with login only."` **Fehler-Struktur** (HTTP 200, aber Fehler unter `a00.ex`):** ```json {"a00": {"ex": {"ex": {"FiZClassId": "17", "message": "..."}}}} ``` → `fw_client.py` prüft `a00.ex` und wirft `FamilyWallError`. **Response (Erfolg):** ``` a00.r.r → Einladungsobjekt (Struktur unbekannt, da nicht testbar mit Test-Account) ``` **Bekannte Enum-Werte für `role`:** - `"Unknown"` ✓ verifiziert - `"Parent"`, `"Child"` → `FamilyRoleTypeEnum` decode error **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ### `accupdatefamily` – Kreis umbenennen POST https://api.familywall.com/api/accupdatefamily **Body-Parameter:** | Parameter | Pflicht | Wert | |---|---|---| | `name` | ja | Neuer Kreis-Name | **Hinweis:** Aktualisiert immer den PRIMARY Kreis des Accounts (ignoriert `id`/`familyId` Parameter). **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 ## Offene Punkte - Unlike-Endpoint (Service Worker blockiert Analyse) - Erinnerungen (reminder) – nur Premium-Account - Wiederholungen (repeat) – nur Premium-Account - Sortierung von Kategorien via API - mpadditemtolist (Zutaten aus Rezept → Einkaufsliste) - Einladung bestehender FamilyWall-Nutzer (accinvite nur für neue Accounts) - Kreis-Delete-Endpoint (API: "delete not supported" für family-Objekte)