- create_task: new optional params due_date (ISO 8601) and assignee_ids (list[str]) - update_task: new optional params due_date, assignee_ids, and list_id (move task) - get_tasks: returns due_date and assignee_ids fields per task - API params verified via FW_DEBUG=1: dueDate, assignee, taskListId - SPEC.md, README, CLAUDE.md updated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
23 KiB
Family Wall API – Spezifikation
Erarbeitet durch Browser-Traffic-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 |
nicht senden — wird als undefined ignoriert (verifiziert per JS-Analyse) |
clientId |
weglassen |
clientSecret |
weglassen |
generateAutologinToken |
weglassen |
countryCode |
weglassen |
Response (Erfolg):
{ "a00": { "r": { "r": <SessionObject> }, "cn": "log2in" } }
SessionObject enthält u.a. tokenCsrf und webApiUrl.
tokenCsrf ist die Session-ID – identisch zur JSESSIONID im Cookie.
Response (Fehler):
{ "ex": { "ex": <ErrorObject> } }
{ "un": { "un": <ErrorObject> } }
Der Server setzt nach erfolgreichem Login ein Session-Cookie: Set-Cookie: JSESSIONID= (= tokenCsrf)
Folgecalls (nach Login)
Alle API-Calls nach dem Login benötigen:
| Cookie | JSESSIONID=<session-id> |
| Header | Tokencsrf: <session-id> (identisch zur JSESSIONID) |
| Content-Type | application/x-www-form-urlencoded |
Logout
POST https://api.familywall.com/api/log2out Content-Type: application/x-www-form-urlencoded
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 POST /api/log2out → Session invalidieren
Credentials (E-Mail + Passwort) werden einmalig via mcp-familywall setup
im OS Keyring gespeichert (Keys: email, password). Kein Keyring-Eintrag
für session_id.
Bekannte Endpoints
famlistfamily – Kreise abrufen
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
Response-Struktur: zu verifizieren beim ersten echten Call
accgetallfamily – Listen + Tasks abrufen
POST https://api.familywall.com/api/accgetallfamily Content-Type: application/x-www-form-urlencoded
Body-Parameter:
| Parameter | Wert |
|---|---|
a01call |
"taskcategorysync" |
a02call |
"tasksync" |
Hinweis: a03call=tasklistsync ist kein gültiger Endpoint — API antwortet mit
"The call tasklistsync is not registered". Nicht verwenden.
partnerScope, a03id, withStateBean werden weggelassen.
Response-Struktur (verifiziert):
a00 → famlistfamily-Daten (Kreise inkl. members[]) – Nebeneffekt
a01.r.r.updatedCreated[] → taskcategorysync (Kategorien/Abteilungen pro Liste)
.metaId → Kategorie-ID (Format taskCategory/<familyId>_<sysId>)
.name → Kategoriename (sprachabhängig, z.B. "Beverages")
.emoji → Emoji-Symbol der Kategorie
.systemCategoryId → numerische System-ID (sprach-unabhängig)
.taskListType → Listentyp der Kategorie (z.B. "SHOPPING_LIST")
**Wichtig:** Alle 171 Kategorien sind SHOPPING_LIST —
es gibt keine TODO-Kategorien in der API
.sortingIndexByTaskList → dict: Listen-ID → Sortierposition
**Achtung:** enthält ALLE Listen-IDs unabhängig vom Typ
→ NICHT für Typ-Filterung verwenden!
Stattdessen: taskListType der Kategorie mit
taskListType der Liste (aus taskgettasklists) vergleichen
.locale → Sprache des Namens (z.B. "de", "en", "ru", "fr", "es",
"it", "nl", "pt", "sv", "ko", "ja")
Jede Sprache = eigener Eintrag mit eigenem metaId/systemCategoryId
**Wichtig:** Custom-Kategorien (rights.canDelete='true') haben
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):
| 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 numerischesystemCategoryId-Teil (z.B.200) führt zu API-Fehler"cannot find task category id=200". - Das
categories[]-Feld in der Response zeigt immerSYS-CAT-SHOPPINGLIST(Listen-Level-Systemkategorie, unabhängig vom gesetztentaskCategoryId). Die tatsächliche Task-Kategorie ist im FeldtaskCategoryIdder 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 Content-Type: application/x-www-form-urlencoded
Body-Parameter:
| 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[]
.metaId → ID der Aktivität (= wallMessageId)
.refType → Aktivitätstyp (z.B. STATUS, FAMILY_CREATED)
.text → Text (optional, fehlt bei System-Events)
.creationDate → Datum (ISO 8601)
.accountId → Autor-ID
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)
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
POST https://api.familywall.com/api/taskcreate2 Content-Type: application/x-www-form-urlencoded
Body-Parameter (verifiziert):
| Parameter | Pflicht | Wert |
|---|---|---|
taskListId |
ja | Listen-ID aus get_lists (z.B. taskList/123_456) |
text |
ja | Aufgabentitel |
description |
nein | Optionale Beschreibung |
taskCategoryId |
nein | Kategorie-MetaId aus get_categories (z.B. taskCategory/23431854_200) |
dueDate |
nein | Fälligkeitsdatum ISO 8601 (z.B. "2026-04-30T18:00:00"); gespeichert als dueDate im Task-Objekt |
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 |
Response-Struktur (verifiziert):
a00.r.r → vollständiges Task-Objekt der neu erstellten Task
.metaId → eindeutige Task-ID (z.B. "task/23431854_726362809")
.taskListId → Listen-ID
.text → Titel
.complete → "false" (immer, direkt nach Erstellung)
taskupdate2 – Task aktualisieren
POST https://api.familywall.com/api/taskupdate2 Content-Type: application/x-www-form-urlencoded
Body-Parameter (verifiziert):
| Parameter | Pflicht | Wert |
|---|---|---|
metaId |
ja | Task-ID aus get_tasks |
text |
nein | Neuer Titel (mindestens eines der optionalen Felder erforderlich) |
description |
nein | Neue Beschreibung |
taskCategoryId |
nein | Kategorie-MetaId aus get_categories (z.B. taskCategory/23431854_200) |
dueDate |
nein | Fälligkeitsdatum ISO 8601 (z.B. "2026-04-30T18:00:00"); gespeichert als dueDate im Task-Objekt |
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 |
taskListId |
nein | Ziel-Listen-ID zum Verschieben des Tasks (verifiziert – ändert taskListId im Task-Objekt) |
Hinweis: taskListId ist optional – ohne diesen Parameter bleibt der Task in seiner aktuellen Liste.
Response-Struktur: kein spezifischer Rückgabewert – Erfolg = kein ex/un-Key auf Top-Level.
taskmark – Task als erledigt/offen markieren
POST https://api.familywall.com/api/taskmark Content-Type: application/x-www-form-urlencoded
Body-Parameter (verifiziert):
| Parameter | Pflicht | Wert |
|---|---|---|
taskId |
ja | Task-ID aus get_tasks (WICHTIG: taskId, nicht metaId!) |
complete |
ja | "true" oder "false" (String, nicht Boolean!) |
Achtung: Der Endpoint heißt intern taskId, nicht metaId.
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")
metadelete – Objekt löschen
POST https://api.familywall.com/api/metadelete Content-Type: application/x-www-form-urlencoded
Body-Parameter (verifiziert):
| Parameter | Pflicht | Wert |
|---|---|---|
id |
ja | Task-ID aus get_tasks (WICHTIG: id, nicht metaId!) |
Hinweis: metadelete ist ein generischer Lösch-Endpoint für beliebige Objekte (Tasks, etc.).
Entsprechend vorsichtig verwenden.
Response-Struktur (verifiziert):
a00.r.r → "true" (String)
Fehlerverhalten: Bei falschem Parameter-Namen (metaId, taskId etc.) antwortet die API
mit {"a00": {"un": {"un": {"message": "missing value in: id"}}}} auf Top-Level ohne un-Key
→ wird vom fw_client fälschlich als Erfolg interpretiert. Daher ist der korrekte Parameter-Name
kritisch.
taskcategoryput – Kategorie erstellen
POST https://api.familywall.com/api/taskcategoryput Content-Type: application/x-www-form-urlencoded
Body-Parameter (verifiziert via FW_DEBUG=1):
| Parameter | Pflicht | Wert |
|---|---|---|
name |
ja | Kategorie-Name (beliebiger String) |
emoji |
nein | Icon: Unicode-Emoji-Zeichen (z.B. 🌿) oder beliebiger String-Code (z.B. "FOOD") — wird as-is gespeichert |
Hinweise:
- Die neue Kategorie wird allen Listen der Familie zugeordnet — es gibt keine per-Liste-Einschränkung.
- Benutzerdefinierte Kategorien haben
systemCategoryId=nullundrights.canDelete='true'. - System-Kategorien haben
rights.canDelete=null— API erlaubt Löschen, aberdelete_categoryTool verweigert es.
Response-Struktur (verifiziert):
a00.r.r → vollständiges Kategorie-Objekt
.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:
{"a00": {"un": {"un": {"FiZClassId": "502", "message": "cat without a name ..."}}}}
taskcategorydelete – Kategorie löschen
POST https://api.familywall.com/api/taskcategorydelete Content-Type: application/x-www-form-urlencoded
Body-Parameter (verifiziert via FW_DEBUG=1):
| Parameter | Pflicht | Wert |
|---|---|---|
id |
ja | Kategorie-MetaId aus get_categories (WICHTIG: id, nicht metaId!) |
Achtung: Falscher Parameter-Name metaId führt zu:
{"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):
wallmoodmitmoodType: "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
STATUSwirksam;FAMILY_CREATEDund 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 |
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 ist dadurch nicht per Browser-DevTools inspizierbar. Unlike bleibt bis zur weiteren Analyse nicht unterstützt.
Response-Struktur (verifiziert):
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):
| Feld | Typ | Bedeutung |
|---|---|---|
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 Speicherpfad ist nur einer gesetzt. Immer beide prüfen.
Silent-Fail-Szenarien (API antwortet 200, aber Like wird nicht gesetzt):
- Self-Like-Restriction: Eigener Post kann nicht geliked werden
(verifiziert: Account 23431898 kann Post
wall/23431854_31119189nicht liken, obwohl API regulär antwortet —modifDatebleibt 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
AND moodStarShortcut: false AND moodMap: {}.
Noch zu verifizieren
Exakter Wert für→ nicht senden (verifiziert per JS-Analyse)type-Parameter beim LoginResponse-Struktur von→ a00.r.r[], metaId + name (verifiziert)famlistfamily(Kreise)Ob→ nein, kein gültiger Endpoint (verifiziert)a03call=tasklistsyncbenötigt wird- Listen-IDs aus
a01.r.r.updatedCreated[].sortingIndexByTaskList-Keys (verifiziert) - Listen-Namen und Zähler (remainingTaskNumber, totalTaskNumber) → noch unbekannt
- Kreis-Zuordnung in
accgetallfamily-Response → noch offen Ob→ nein (verifiziert)partnerScope/withStateBeanbenötigt werden- Session-Lebensdauer (irrelevant da kein Caching)
→taskcreate2: Response-Struktura00.r.r= vollständiges Task-Objekt (verifiziert)→ nein, nicht erforderlich (verifiziert)taskupdate2: obtaskListIdPflichtfeld ist→taskmark: korrekter Parameter-NametaskId(nichtmetaId!) (verifiziert)→metadelete: korrekter Parameter-Name + Response-Strukturid, Response"true"(verifiziert)→wallmood: Parameter-NamewallIdwall_message_id(verifiziert via API-Fehlermeldung)→ verifiziert: idempotentes SET mitwallmood:moodType-Werte, Toggle vs. explizit, Response-Struktur"STAR", kein Toggle (siehe oben)wallmoodUnlike: Mechanismus unbekannt — Service Worker verhindert Browser-Inspektion; alle getesteten Ansätze fehlgeschlagen (siehe oben)→taskcreate2/taskupdate2: Kategorie-Paramter-NametaskCategoryId, Wert = vollständige metaId (verifiziert)→taskcategoryput: Body-Parameter, Response-Strukturname(Pflicht),emoji(optional), Response = neues Kategorie-Objekt (verifiziert)→taskcategorydelete: Body-Parameterid(nichtmetaId!), Response ="true"(verifiziert)→taskcreate2/taskupdate2: Fälligkeitsdatum-ParameterdueDate, ISO 8601 String, gespeichert alsdueDateim Task-Objekt (verifiziert)→taskcreate2/taskupdate2: Zuweisung-Parameterassignee(Member-ID String; mehrere Werte = mehrfach senden); gespeichert alsassigneeIds[](verifiziert)→taskupdate2: Task verschiebentaskListIdals optionaler Parameter setzt neue Liste (verifiziert)taskupdate2: Alle Zuweisungen entfernen (leereassignee-Liste) → noch nicht verifiziert