Files
mcp-familywall/SPEC.md
T
marcus a76dc0fd51 feat: category assignment in create_task / update_task (v0.4.10)
Verified via FW_DEBUG=1 + systematic param-name probing that the
correct parameter is `taskCategoryId` with value = full metaId from
get_categories (e.g. taskCategory/23431854_200).  Numeric systemCategoryId
alone causes API error; full metaId is accepted and stored.

Changes:
- create_task: add optional category_id parameter → sent as taskCategoryId
- update_task: add optional category_id parameter → sent as taskCategoryId;
  guard now accepts category_id-only updates
- get_tasks: expose category_id field in returned task objects
- get_categories: update docstring (param name now known)
- SPEC.md: document verified taskCategoryId param + clarify categories[]
  vs taskCategoryId field distinction
- scripts/find_category_param.py: discovery script used to find param name

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

18 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
  .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.

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)