Dokumente aktualisiert
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 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.
|
||||
|
||||
## Base URL
|
||||
@@ -18,21 +18,14 @@ Content-Type: application/x-www-form-urlencoded
|
||||
|---|---|
|
||||
| `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 |
|
||||
| `type` | `"email"` (verifiziert) |
|
||||
|
||||
**Response (Erfolg):**
|
||||
|
||||
```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):**
|
||||
|
||||
```json
|
||||
@@ -41,12 +34,10 @@ Content-Type: application/x-www-form-urlencoded
|
||||
```
|
||||
|
||||
Der Server setzt nach erfolgreichem Login ein Session-Cookie:
|
||||
Set-Cookie: JSESSIONID=<session-id> (= tokenCsrf)
|
||||
`Set-Cookie: JSESSIONID=<session-id>`
|
||||
|
||||
### Folgecalls (nach Login)
|
||||
|
||||
Alle API-Calls nach dem Login benötigen:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Cookie** | `JSESSIONID=<session-id>` |
|
||||
@@ -55,7 +46,6 @@ Alle API-Calls nach dem Login benötigen:
|
||||
|
||||
### Logout
|
||||
POST https://api.familywall.com/api/log2out
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
POST /api/log2in → Session-ID
|
||||
POST /api/<endpoint> → Nutzdaten
|
||||
POST /api/<endpoint> → 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`). Kein Keyring-Eintrag
|
||||
für `session_id`.
|
||||
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 abrufen
|
||||
### `famlistfamily` – Kreise + Mitglieder 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
|
||||
**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
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter:**
|
||||
|
||||
@@ -127,425 +121,204 @@ Content-Type: application/x-www-form-urlencoded
|
||||
| `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):**
|
||||
**Response-Struktur:**
|
||||
```
|
||||
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
|
||||
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[]
|
||||
```
|
||||
|
||||
**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
|
||||
### `wallget` – Wall-Aktivitäten 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):**
|
||||
**Response-Struktur:**
|
||||
```
|
||||
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
|
||||
.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 → {"<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
|
||||
POST https://api.familywall.com/api/taskcreate2
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| 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 |
|
||||
| `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-Struktur (verifiziert):**
|
||||
**Response:**
|
||||
```
|
||||
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)
|
||||
a00.r.r → vollständiges Task-Objekt
|
||||
.metaId → neue Task-ID
|
||||
```
|
||||
|
||||
### `taskupdate2` – Task aktualisieren
|
||||
POST https://api.familywall.com/api/taskupdate2
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| 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) |
|
||||
| `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 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.
|
||||
|
||||
**dueDate Clearing – `"$empty"` Sentinel (verifiziert April 2026, v0.4.16):**
|
||||
|
||||
Der FiZ-Framework verwendet den Sentinel-Wert `"$empty"` um optionale Felder zu leeren.
|
||||
**Hinweis:** `taskListId` ist NICHT Pflicht beim Update.
|
||||
|
||||
**Response:**
|
||||
```
|
||||
POST taskupdate2
|
||||
metaId=task/23431854_726333919&dueDate=$empty
|
||||
→ dueDate wird auf null gesetzt ✓
|
||||
a00.r.r → vollständiges Task-Objekt
|
||||
```
|
||||
|
||||
`update_task(clear_due_date=True)` sendet intern `dueDate=$empty`.
|
||||
|
||||
**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
|
||||
### `taskmark` – Task abhaken/wiedereröffnen
|
||||
POST https://api.familywall.com/api/taskmark
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `taskId` | ja | Task-ID aus `get_tasks` (**WICHTIG: `taskId`, nicht `metaId`!**) |
|
||||
| `complete` | ja | `"true"` oder `"false"` (String, nicht Boolean!) |
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| `taskId` | Task-metaId ⚠️ nicht `metaId`! |
|
||||
| `complete` | `"true"` oder `"false"` (String!) |
|
||||
|
||||
**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):**
|
||||
**Response:**
|
||||
```
|
||||
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
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `id` | ja | Task-ID aus `get_tasks` (**WICHTIG: `id`, nicht `metaId`!**) |
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| `id` | Task-metaId ⚠️ nicht `metaId`! |
|
||||
|
||||
Hinweis: `metadelete` ist ein generischer Lösch-Endpoint für beliebige Objekte (Tasks, etc.).
|
||||
Entsprechend vorsichtig verwenden.
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
**Response:**
|
||||
```
|
||||
a00.r.r → "true" (String)
|
||||
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` – Post liken
|
||||
POST https://api.familywall.com/api/wallmood
|
||||
|
||||
### `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
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert via FW_DEBUG=1):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| 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 |
|
||||
| `name` | ja | Kategoriename |
|
||||
| `emoji` | nein | Unicode-Emoji oder beliebiger String |
|
||||
|
||||
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.
|
||||
**Hinweis:** Kategorien sind family-wide – `listId` hat keine Wirkung.
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
**Response:**
|
||||
```
|
||||
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:
|
||||
```json
|
||||
{"a00": {"un": {"un": {"FiZClassId": "502", "message": "cat without a name ..."}}}}
|
||||
a00.r.r → Kategorie-Objekt mit metaId
|
||||
```
|
||||
|
||||
### `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):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Pflicht | 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 |
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| `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 |
|
||||
| `id` | Kategorie-metaId ⚠️ nicht `metaId`! |
|
||||
|
||||
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.
|
||||
**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`.
|
||||
|
||||
**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"
|
||||
```
|
||||
### `taskgettasklists` – Listen abrufen (alternativ)
|
||||
POST https://api.familywall.com/api/taskgettasklists
|
||||
|
||||
**Like-Zustand bestimmen (zwei Indikatoren, beide auswerten):**
|
||||
Wird intern zur Verifikation von `taskListType` genutzt.
|
||||
|
||||
| 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 |
|
||||
## Systembezeichnungen für Listen-Namen
|
||||
|
||||
Beide Indikatoren können den Like-Zustand korrekt abbilden — je nach API-internem
|
||||
Speicherpfad ist nur einer gesetzt. Immer beide prüfen.
|
||||
| Systembezeichnung | Deutsch |
|
||||
|---|---|
|
||||
| `SYS-CAT-SHOPPINGLIST` | `Einkaufsliste` |
|
||||
| `SYS-CAT-TODOLIST` | `Aufgaben` |
|
||||
|
||||
**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
|
||||
## Debug-Logging
|
||||
|
||||
Erkennungsmerkmal für Silent-Fail: `modifDate` im Response identisch zum Vorherigen
|
||||
AND `moodStarShortcut: false` AND `moodMap: {}`.
|
||||
Wenn die Umgebungsvariable `FW_DEBUG=1` gesetzt ist, loggt `fw_client.py`
|
||||
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)
|
||||
- ~~Response-Struktur von `famlistfamily` (Kreise)~~ → a00.r.r[], metaId + name (verifiziert)
|
||||
- ~~Ob `a03call=tasklistsync` benötigt wird~~ → **nein**, 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-Struktur~~ → `a00.r.r` = vollständiges Task-Objekt (verifiziert)
|
||||
- ~~`taskupdate2`: ob `taskListId` Pflichtfeld ist~~ → **nein**, nicht erforderlich (verifiziert)
|
||||
- ~~`taskmark`: korrekter Parameter-Name~~ → **`taskId`** (nicht `metaId`!) (verifiziert)
|
||||
- ~~`metadelete`: korrekter Parameter-Name + Response-Struktur~~ → **`id`**, Response `"true"` (verifiziert)
|
||||
- ~~`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)
|
||||
- `wallmood` Unlike: Mechanismus unbekannt — Service Worker verhindert Browser-Inspektion; alle getesteten Ansätze fehlgeschlagen (siehe oben)
|
||||
- ~~`taskcreate2` / `taskupdate2`: Kategorie-Paramter-Name~~ → **`taskCategoryId`**, Wert = vollständige metaId (verifiziert)
|
||||
- ~~`taskcategoryput`: Body-Parameter, Response-Struktur~~ → `name` (Pflicht), `emoji` (optional), Response = neues Kategorie-Objekt (verifiziert)
|
||||
- ~~`taskcategorydelete`: Body-Parameter~~ → **`id`** (nicht `metaId`!), Response = `"true"` (verifiziert)
|
||||
- ~~`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)
|
||||
- ~~`taskupdate2`: Task verschieben~~ → **`taskListId`** als optionaler Parameter setzt neue Liste (verifiziert)
|
||||
- `taskupdate2`: Alle Zuweisungen entfernen (leere `assignee`-Liste) → noch nicht verifiziert
|
||||
- ~~`taskupdate2`: `dueDate` entfernen (Clearing)~~ → **`dueDate=$empty`** (FiZ-Sentinel, verifiziert April 2026, v0.4.16)
|
||||
**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.
|
||||
|
||||
## Offene Punkte
|
||||
|
||||
- Unlike-Endpoint (Service Worker blockiert Analyse)
|
||||
- Erinnerungen (reminder) – nur Premium-Account
|
||||
- Wiederholungen (repeat) – nur Premium-Account
|
||||
- Sortierung von Kategorien via API
|
||||
|
||||
Reference in New Issue
Block a user