6c955d67c8
- fw_client: detect nested a00.un errors (previously silent-failed as success) - update_task: add clear_due_date=True parameter that returns a clear error explaining the Family Wall API cannot clear dueDate once set - SPEC.md: document all tested clearing candidates and their API responses, add Fehlerstruktur-Varianten section - Verified: dueDate cannot be removed via any form-encoded value; all invalid date strings are rejected via a00.un.un (silently swallowed before this fix) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
546 lines
25 KiB
Markdown
546 lines
25 KiB
Markdown
# 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):**
|
||
|
||
```json
|
||
{ "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):**
|
||
|
||
```json
|
||
{ "ex": { "ex": <ErrorObject> } }
|
||
{ "un": { "un": <ErrorObject> } }
|
||
```
|
||
|
||
Der Server setzt nach erfolgreichem Login ein Session-Cookie:
|
||
Set-Cookie: JSESSIONID=<session-id> (= 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/<endpoint> → 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 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)
|
||
|
||
## 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):**
|
||
|
||
| 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.
|
||
|
||
**dueDate Clearing – nicht implementierbar (Stand: April 2026):**
|
||
|
||
Ausgiebig getestete Werte für `dueDate`, die alle fehlschlugen:
|
||
|
||
| Wert | API-Response |
|
||
|---|---|
|
||
| `""` (leerer String) | `a00.un.un: " is not a valid Date"` |
|
||
| `"null"` | `a00.un.un: "Cannot parse date val=null"` |
|
||
| `"0"` | API-Fehler (ungültiges Datum) |
|
||
| `"-1"` | `a00.un.un: "-1 is not a valid Date"` |
|
||
| `"remove"` / `"clear"` | `a00.un.un: "Cannot parse date val=..."` |
|
||
| `"0000-00-00T00:00:00"` | API-Fehler |
|
||
| `"1970-01-01T00:00:00"` | **Erfolgreich** – setzt dueDate auf Unix-Epoch (kein echtes Clearing!) |
|
||
| Separate Parameter (`removeDueDate`, `clearDueDate`, `clearFields`) | werden ignoriert, dueDate bleibt unverändert |
|
||
| Feld weglassen (kein `dueDate` in Request) | dueDate bleibt unverändert |
|
||
|
||
**Wichtig:** Die Fehler werden als `a00.un.un` zurückgegeben (nicht Top-Level `un`).
|
||
Der `fw_client` prüfte ursprünglich nur Top-Level `un` → silent fail (ab v0.4.15 behoben:
|
||
`a00.un` wird ebenfalls erkannt und als `FamilyWallError` geworfen).
|
||
|
||
Das `dueDate`-Feld kann einmal gesetzt nicht mehr entfernt werden.
|
||
`update_task(clear_due_date=True)` gibt eine klare Fehlermeldung zurück.
|
||
|
||
### `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:
|
||
```json
|
||
{"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:
|
||
```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 |
|
||
|---|---|
|
||
| `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 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)~~ → **nicht möglich** (verifiziert, April 2026) — alle getesteten Werte werden als ungültiges Datum abgelehnt (siehe unten) |