Files
mcp-familywall/SPEC.md
T
marcus 0d8036fd4a fix: get_categories always includes custom categories (v0.4.12)
Custom categories (rights.canDelete=true) have no locale field set by
the API and were silently excluded by the locale filter. They now bypass
both the locale and taskListType filters so they always appear in
get_categories output regardless of the locale parameter.

Also: deleted 7 test categories (TEmojiApple, Obst & Gemüse (old),
TestKategorie, ProbeKat2, [TEST]emoji=apple, ProbeKat1, TDelMeta)
and restored 'Obst & Gemüse' (emoji 🍎) as a clean custom category.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 08:06:21 +02:00

21 KiB
Raw Blame History

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 / .assigneeIds            → zugewiesene Mitglieder
  .reminder                           → Erinnerungsdatum (ISO 8601, optional)
  .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 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 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)

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)

Hinweis: taskListId ist nicht erforderlich (verifiziert Update ohne taskListId funktioniert).

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=null und rights.canDelete='true'.
  • System-Kategorien haben rights.canDelete=null — API erlaubt Löschen, aber delete_category Tool 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):

  • 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
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_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 AND moodStarShortcut: false AND moodMap: {}.

Noch zu verifizieren

  • Exakter Wert für type-Parameter beim Login → nicht senden (verifiziert per JS-Analyse)
  • Response-Struktur von famlistfamily (Kreise) → a00.r.r[], metaId + name (verifiziert)
  • Ob a03call=tasklistsync benötigt wirdnein, kein gültiger Endpoint (verifiziert)
  • 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 partnerScope / withStateBean benötigt werden → nein (verifiziert)
  • Session-Lebensdauer (irrelevant da kein Caching)
  • taskcreate2: Response-Struktura00.r.r = vollständiges Task-Objekt (verifiziert)
  • taskupdate2: ob taskListId Pflichtfeld istnein, nicht erforderlich (verifiziert)
  • taskmark: korrekter Parameter-NametaskId (nicht metaId!) (verifiziert)
  • metadelete: korrekter Parameter-Name + Response-Strukturid, Response "true" (verifiziert)
  • wallmood: Parameter-Name wallIdwall_message_id (verifiziert via API-Fehlermeldung)
  • wallmood: moodType-Werte, Toggle vs. explizit, Response-Struktur → verifiziert: idempotentes SET mit "STAR", kein Toggle (siehe oben)
  • wallmood Unlike: 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 (nicht metaId!), Response = "true" (verifiziert)