35cbfd3061
- Fix add_comment response parser: corrected to a00.r.r.comment.commentId structure - Fix add_comment error handling: return errors directly instead of warnings - Add delete_wall_post: permanently delete wall posts via metadelete endpoint - Add delete_wall_post to README.md Wall & Activities section - Update SPEC.md with correct walladdComment response structure - Update SPEC.md metadelete to include wall posts as supported type - Update CHANGELOG.md with v1.3.1 bugfixes and additions - Version bumped to 1.3.1 in pyproject.toml Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1012 lines
37 KiB
Markdown
1012 lines
37 KiB
Markdown
# Family Wall API – Spezifikation
|
||
|
||
Erarbeitet durch Browser-Traffic-Analyse und React-Fiber-Analyse (April 2026).
|
||
Es gibt keine offizielle API-Dokumentation.
|
||
|
||
**Stand: v1.0.0 (2026-04-17)**
|
||
|
||
## 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` | `"email"` (verifiziert) |
|
||
|
||
**Response (Erfolg):**
|
||
|
||
```json
|
||
{ "r": { "r": <SessionObject> } }
|
||
```
|
||
|
||
**Response (Fehler):**
|
||
|
||
```json
|
||
{ "ex": { "ex": <ErrorObject> } }
|
||
{ "un": { "un": <ErrorObject> } }
|
||
```
|
||
|
||
Der Server setzt nach erfolgreichem Login ein Session-Cookie:
|
||
`Set-Cookie: JSESSIONID=<session-id>`
|
||
|
||
### Folgecalls (nach Login)
|
||
|
||
| | |
|
||
|---|---|
|
||
| **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
|
||
|
||
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 (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`).
|
||
|
||
### Performance-Hinweis: Bulk-Operationen
|
||
|
||
Bulk-Operationen (z.B. alle Tasks einer Liste löschen) bündeln mehrere Calls
|
||
in einer einzigen Session: Login → Call1 → Call2 → ... → CallN → Logout.
|
||
Das spart N−1 Login/Logout-Roundtrips gegenüber N separaten Tool-Aufrufen.
|
||
|
||
## 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 + Mitglieder abrufen
|
||
POST https://api.familywall.com/api/famlistfamily
|
||
|
||
**Body-Parameter:** keine
|
||
|
||
**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, Kategorien abrufen
|
||
POST https://api.familywall.com/api/accgetallfamily
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `a01call` | `"taskcategorysync"` |
|
||
| `a02call` | `"tasksync"` |
|
||
|
||
**Response-Struktur:**
|
||
```
|
||
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[]
|
||
```
|
||
|
||
### `wallget` – Wall-Aktivitäten abrufen
|
||
POST https://api.familywall.com/api/wallget
|
||
|
||
**Response-Struktur:**
|
||
```
|
||
a00.r.r[]
|
||
.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
|
||
```
|
||
|
||
### `taskcreate2` – Task erstellen
|
||
POST https://api.familywall.com/api/taskcreate2
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `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:**
|
||
```
|
||
a00.r.r → vollständiges Task-Objekt
|
||
.metaId → neue Task-ID
|
||
```
|
||
|
||
### `taskupdate2` – Task aktualisieren
|
||
POST https://api.familywall.com/api/taskupdate2
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `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) |
|
||
| `recurrency` | nein | `"DAILY"` \| `"WEEKLY"` \| `"MONTHLY"` \| `"YEARLY"` \| `"NONE"` (entfernen) |
|
||
| `recurrencyInterval` | nein | int (z.B. `2` für "alle 2 Wochen") |
|
||
| `rrule` | nein | vollständige iCal-RRULE (z.B. `"FREQ=WEEKLY;INTERVAL=2;BYDAY=FR"`) |
|
||
| `byDay` | nein | z.B. `"FR"`, `"1SA"`, `"MO,TU,WE,TH,FR"` |
|
||
| `byMonthDay` | nein | int (z.B. `7` für "am 7. des Monats") |
|
||
| `recurrencyEndDate` | nein | ISO-Datum (z.B. `"2026-12-31"`) |
|
||
| `endOccurence` | nein | int (nach N Wiederholungen aufhören) |
|
||
**Hinweis:** `taskListId` ist NICHT Pflicht beim Update.
|
||
|
||
**⚠️ Encoding:** Der FiZ-`Ai()`-Encoder serialisiert alle Felder **flach** als Top-Level-Form-Parameter.
|
||
Recurrency-Felder werden direkt auf Top-Level gesendet (verifiziert via xb-Encoder im JS-Bundle).
|
||
|
||
**Reminder-Schreibzugriff (verifiziert 2026-04-17):**
|
||
|
||
Reminder werden via Dot-Notation als Unterfelder gesendet:
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `reminder.reminderUnit` | `"MINUTE"` \| `"HOUR"` \| `"DAY"` (WEEK wird abgelehnt) |
|
||
| `reminder.reminderValue` | nicht-negativer Integer als String (z.B. `"30"`) |
|
||
| `reminder.reminderType` | `"SNOOZE"` (aktiv) oder `"NONE"` (Reminder entfernen) |
|
||
| `reminder.localId` | optional; `"0"` akzeptiert |
|
||
|
||
**Wichtig:**
|
||
- Nur Dot-Notation funktioniert. Flache Top-Level-Keys (`reminderUnit`, `reminderValue`, …),
|
||
JSON-String (`reminder={…}`) und Bracket-Notation (`reminder[reminderUnit]`) werden
|
||
**silent-ignored** (HTTP 200, Reminder unverändert).
|
||
- Partielle Reminder-Updates (z.B. nur `reminder.reminderType=NONE`) liefern
|
||
`task reminder invalid` — immer vollständigen Block senden.
|
||
- `reminder=$empty` wird silent-ignored; zum Entfernen muss `reminder.reminderType=NONE`
|
||
mit `reminder.reminderValue=0` und `reminder.reminderUnit=MINUTE` gesendet werden.
|
||
- Reminder-Felder lassen sich in einem einzigen `taskupdate2`-Call gemeinsam mit
|
||
`recurrency`/`dueDate`/`text` setzen — keine Interferenz.
|
||
- `$empty` auf Unterfelder (`reminder.reminderValue=$empty`) führt zu Parse-Fehlern.
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → vollständiges Task-Objekt
|
||
```
|
||
|
||
### `taskmark` – Task abhaken/wiedereröffnen
|
||
POST https://api.familywall.com/api/taskmark
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `taskId` | Task-metaId ⚠️ nicht `metaId`! |
|
||
| `complete` | `"true"` oder `"false"` (String!) |
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → Task-Objekt mit lastAction: "MARK_COMPLETED"
|
||
```
|
||
|
||
### `metadelete` – Objekt löschen (Task, Rezept, Essensplan-Eintrag)
|
||
POST https://api.familywall.com/api/metadelete
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `id` | metaId des zu löschenden Objekts ⚠️ nicht `metaId`! |
|
||
|
||
**Unterstützte Objekt-Typen:**
|
||
- Tasks: `task/<id>`
|
||
- Rezepte: `recipe/<id>`
|
||
- Essensplan-Einträge: `dish/<id>` und `meal/<id>`
|
||
- Wall-Posts: `wall/<id>` (v1.3.1+)
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → "true" (String)
|
||
```
|
||
|
||
### `wallmood` – Post liken / unlike
|
||
POST https://api.familywall.com/api/wallmood
|
||
|
||
**Body-Parameter (Like):**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `wall_message_id` | Post-metaId ⚠️ nicht `wallId` oder `id`! |
|
||
| `add` | `"STAR"` (Mood-Typ setzen) |
|
||
| `remove` | `"$empty"` (Sentinel für „nichts entfernen") |
|
||
|
||
**Body-Parameter (Unlike):**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `wall_message_id` | Post-metaId |
|
||
| `add` | `"$empty"` (Sentinel für „nichts hinzufügen") |
|
||
| `remove.0` | `"STAR"` (Array-Dot-Notation — `remove` ist ein Array) |
|
||
|
||
**Bekannte Einschränkungen:**
|
||
- Self-Like: API antwortet 200, macht aber serverseitig nichts
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → Wall-Objekt mit moodMap, refAction: "MOOD_STAR"
|
||
```
|
||
|
||
**Verifiziert am:** 2026-04-17 via Network-Interceptor (echter Request-Body)
|
||
|
||
### `wallpublish` – Wall-Post veröffentlichen
|
||
|
||
POST https://api.familywall.com/api/wallpublish
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `tagline` | Post-Text |
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → Wall-Post-Objekt
|
||
.metaId → neue Post-ID
|
||
.tagline → Post-Text
|
||
.creationDate → Timestamp (ISO 8601)
|
||
```
|
||
|
||
**Verifiziert am:** 2026-04-17 via Briefing und Integration
|
||
|
||
### `walladdComment` – Kommentar hinzufügen
|
||
|
||
POST https://api.familywall.com/api/walladdComment
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `wall_message_id` | Post-metaId (z.B. `wall/23431854_31119189`) |
|
||
| `comment` | Kommentartext |
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r.comment → Kommentar-Objekt (nested)
|
||
.commentId → neue Kommentar-ID
|
||
.text → Kommentartext
|
||
.creationDate → Timestamp (ISO 8601)
|
||
```
|
||
|
||
**Bekannte Einschränkungen:**
|
||
- `mood` und `clientOpId` sind optional und werden ignoriert
|
||
|
||
**Verifiziert am:** 2026-04-17 via Briefing und Integration (Response-Struktur in v1.3.1 korrekt dokumentiert)
|
||
|
||
### `taskcategoryput` – Kategorie erstellen/aktualisieren
|
||
POST https://api.familywall.com/api/taskcategoryput
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `name` | ja | Kategoriename |
|
||
| `emoji` | nein | Unicode-Emoji oder beliebiger String |
|
||
|
||
**Hinweis:** Kategorien sind family-wide – `listId` hat keine Wirkung.
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → Kategorie-Objekt mit metaId
|
||
```
|
||
|
||
### `taskcategorydelete` – Kategorie löschen
|
||
POST https://api.familywall.com/api/taskcategorydelete
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `id` | Kategorie-metaId ⚠️ nicht `metaId`! |
|
||
|
||
**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`.
|
||
|
||
### `taskcreatelist` – Liste erstellen
|
||
POST https://api.familywall.com/api/taskcreatelist
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `name` | ja | Listen-Name (max 200 Zeichen) |
|
||
| `taskListType` | ja | `"SHOPPING_LIST"` oder `"TODOS"` |
|
||
| `sharedToAll` | nein | `"true"` / `"false"` (default: `"true"`) |
|
||
| `color` | nein | Hex-Farbwert z.B. `"#4784EC"` |
|
||
| `emoji` | nein | Unicode-Emoji z.B. `"🛒"` |
|
||
| `scope` | nein | Kreis-metaId z.B. `"family/23447378"` (ohne scope → primärer Kreis) |
|
||
|
||
**Scope-Verhalten:**
|
||
- Ohne `scope`: Liste wird im primären Kreis des Accounts erstellt
|
||
- Mit `scope=family/XXXX`: Liste wird im angegebenen Kreis erstellt
|
||
- Die `metaId` der neuen Liste kodiert den Kreis: `taskList/CIRCLENUM_LISTNUM`
|
||
- Parameter `familyId`, `circleId`, `family`, `id` → werden ignoriert, nur `scope` wirkt
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → vollständiges Listen-Objekt
|
||
.metaId → neue Listen-ID (z.B. "taskList/23431854_29759623")
|
||
.name → Listen-Name
|
||
.taskListType → SHOPPING_LIST oder TODOS
|
||
.sharedToAll → "true" / "false"
|
||
.emoji → Unicode-Emoji (fehlt wenn nicht gesetzt)
|
||
.color → Hex-Farbwert z.B. "#E53935" (fehlt wenn nicht gesetzt)
|
||
.familyId → Kreis-metaId des erstellten Liste
|
||
.rights.canDelete → "true" (user-created lists)
|
||
```
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
### `taskupdatelist` – Liste aktualisieren
|
||
POST https://api.familywall.com/api/taskupdatelist
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `metaId` | ja | Listen-metaId ⚠️ nicht `id`! |
|
||
| `name` | nein | Neuer Listen-Name |
|
||
| `color` | nein | Hex-Farbwert z.B. `"#E53935"` |
|
||
| `emoji` | nein | Unicode-Emoji z.B. `"🧪"` |
|
||
| `scope` | nein | Kreis-metaId für sekundäre Kreise; automatisch aus metaId abgeleitet |
|
||
|
||
**Hinweis:** Nur übergebene Felder werden geändert (Partial Update).
|
||
Felder die nicht mitgeschickt werden bleiben auf dem Server unverändert.
|
||
System-Listen (`rights.canUpdate` fehlt oder `!= "true"`) können nicht geändert werden.
|
||
MCP-Server prüft `rights.canUpdate` vor dem Update via `taskgettasklists`.
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → vollständiges Listen-Objekt (analog taskcreatelist)
|
||
.metaId → Listen-ID
|
||
.name → (aktualisierter) Listen-Name
|
||
.taskListType → SHOPPING_LIST oder TODOS
|
||
.emoji → (aktualisiertes) Unicode-Emoji
|
||
.color → (aktualisierter) Hex-Farbwert
|
||
.familyId → Kreis-metaId
|
||
.rights.canUpdate → "true" für bearbeitbare Listen
|
||
.rights.canDelete → "true" für löschbare Listen
|
||
.lastAction → "UPDATED"
|
||
```
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
### `taskdeletelist` – Liste löschen
|
||
POST https://api.familywall.com/api/taskdeletelist
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `id` | ja | Listen-metaId ⚠️ nicht `listId` oder `taskListId`! |
|
||
| `scope` | nein | Kreis-metaId (erforderlich für sekundäre Kreise) |
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → "true" (String)
|
||
```
|
||
|
||
**Hinweis:** Löscht die Liste und alle enthaltenen Tasks unwiderruflich.
|
||
System-Listen (`rights.canDelete` fehlt oder `null`) sind nicht löschbar.
|
||
MCP-Server prüft dies vor dem Löschen via `taskgettasklists`.
|
||
Für Listen in sekundären Kreisen muss `scope=family/XXXX` mitgeschickt werden.
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
### `taskgettasklists` – Listen abrufen
|
||
POST https://api.familywall.com/api/taskgettasklists
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `scope` | nein | Kreis-metaId z.B. `"family/23447378"` (ohne scope → primärer Kreis) |
|
||
|
||
**Scope-Verhalten (Server):**
|
||
- Ohne `scope`: Nur Listen des primären Kreises werden zurückgegeben
|
||
- Mit `scope=family/XXXX`: Nur Listen des angegebenen Kreises
|
||
- Es gibt keinen servereitigen Filter für mehrere Kreise gleichzeitig
|
||
- Andere Parameter (`familyId`, `circleId`, etc.) werden ignoriert
|
||
|
||
**Scope-Verhalten (MCP-Tool get_lists, v0.8.2+):**
|
||
- Ohne `scope`: Der MCP-Server ruft für **jeden Kreis** separate `taskgettasklists(scope=<circle_id>)`-Calls ab und merged die Ergebnisse
|
||
- Mit `scope=family/XXXX` oder `scope="Kreis Name"`: Nur dieser Kreis (wie bisher)
|
||
|
||
**Response-Struktur:**
|
||
```
|
||
a00.r.r[] → Liste aller Task-Listen des Kreises
|
||
.metaId → Listen-ID (z.B. "taskList/23431854_29740942")
|
||
.name → Systembezeichnung oder Benutzer-Name
|
||
.taskListType → SHOPPING_LIST oder TODOS
|
||
.familyId → Kreis-metaId (z.B. "family/23431854")
|
||
.emoji → Unicode-Emoji oder "" (leerer String = kein Emoji)
|
||
.color → Hex-Farbwert z.B. "#E53935" (fehlt wenn nicht gesetzt)
|
||
.remainingTaskNumber → offene Tasks (String)
|
||
.totalTaskNumber → Gesamt-Tasks (String)
|
||
.sharedToAll → "true" / "false"
|
||
.sharedMemberIds[] → Member-accountIds
|
||
.rights.canDelete → "true" = löschbar, fehlt/leer = Systemliste
|
||
.systemId → vorhanden nur bei Systemlisten (z.B. "-10", "-11")
|
||
```
|
||
|
||
**metaId-Encoding:** `taskList/<family_num>_<list_num>` — die `family_num` kodiert den Kreis.
|
||
Beispiel: `taskList/23431854_29740942` gehört zu `family/23431854`.
|
||
|
||
**Hinweis emoji/color:**
|
||
- `emoji`: Systemlisten liefern `""`, user-created Listen liefern den Emoji-String
|
||
oder `""` wenn kein Emoji gesetzt. Normalisierung: `""` → `null` im MCP-Server.
|
||
- `color`: Fehlt komplett wenn nicht gesetzt (nicht `null` oder `""`).
|
||
Normalisierung: fehlendes Feld → `null` im MCP-Server.
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
## Systembezeichnungen für Listen-Namen
|
||
|
||
| Systembezeichnung | Deutsch |
|
||
|---|---|
|
||
| `SYS-CAT-SHOPPINGLIST` | `Einkaufsliste` |
|
||
| `SYS-CAT-TODOLIST` | `Aufgaben` |
|
||
|
||
## Debug-Logging
|
||
|
||
Wenn die Umgebungsvariable `FW_DEBUG=1` gesetzt ist, loggt `fw_client.py`
|
||
vollständige Request-Bodies und Responses nach stderr.
|
||
|
||
**Wichtig:** Keine Secrets in Debug-Ausgaben (Passwort maskieren).
|
||
|
||
**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.
|
||
|
||
### `mprecipeput` – Rezept erstellen
|
||
POST https://api.familywall.com/api/mprecipeput
|
||
|
||
**Wichtig:** Alle Parameter haben das Präfix `recipe.` (verifiziert via FW_DEBUG=1).
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `recipe.name` | ja | Rezeptname |
|
||
| `recipe.isRecipe` | ja | immer `"true"` |
|
||
| `recipe.description` | nein | Kurzbeschreibung |
|
||
| `recipe.ingredients` | nein | Zutaten als Freitext (Zeilen mit `\n` trennen) |
|
||
| `recipe.instructions` | nein | Anleitung als Freitext (Zeilen mit `\n` trennen) |
|
||
| `recipe.prepTime` | nein | Zubereitungszeit in Minuten (String, z.B. `"30"`) |
|
||
| `recipe.cookTime` | nein | Kochzeit in Minuten (String, z.B. `"60"`) |
|
||
| `recipe.serves` | nein | Portionen (String, z.B. `"4"`) |
|
||
| `recipe.url` | nein | externe URL |
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → vollständiges Rezept-Objekt
|
||
.metaId → neue Rezept-ID (z.B. "recipe/23431854_10968866")
|
||
.name → Rezeptname
|
||
.description → Beschreibung
|
||
.ingredients → Zutaten Freitext (API liefert \r\n als Zeilenumbrüche)
|
||
.ingredientsList[] → auto-geparste Zutaten (read-only, vom Server generiert)
|
||
.metaId → "recipeIngredient/<id>"
|
||
.name → Zutatname
|
||
.instructions → Anleitung Freitext
|
||
.prepTime → Zubereitungszeit als String ("30")
|
||
.cookTime → Kochzeit als String ("60")
|
||
.serves → Portionen als String ("4")
|
||
.url → externe URL (fehlt wenn leer)
|
||
.isRecipe → "true"
|
||
.isFavorite → "false"
|
||
.recipeCategories[] → []
|
||
.recipeCategoryIdList[] → []
|
||
.rights.canDelete → "true" für eigene Rezepte
|
||
.rights.canUpdate → "true" für eigene Rezepte
|
||
.familyId, .accountId, .creationDate, .moodMap, .moodStarShortcut
|
||
```
|
||
|
||
**Parser-Bug (v0.11.7):** Das Feld `.ingredientsList[]` wird vom Server geparst und hat einen Bug
|
||
bei Komma+Leerzeichen (z.B. "1, 5g" wird abgeschnitten). Der MCP-Client ignoriert `.ingredientsList`
|
||
und generiert `ingredients_parsed` stattdessen client-seitig durch Splitting von `.ingredients` auf `\r\n`/`\n`.
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
### `mprecipeput` – Rezept aktualisieren (Update)
|
||
POST https://api.familywall.com/api/mprecipeput
|
||
|
||
Identisch zum Create-Aufruf, aber mit zusätzlichem `recipe.metaId`-Parameter.
|
||
Der Server unterscheidet Create vs. Update anhand ob `recipe.metaId` vorhanden ist.
|
||
Nur geänderte Felder müssen mitgeschickt werden (Partial Update).
|
||
`recipe.name` und `recipe.isRecipe="true"` sollten immer mitgeschickt werden.
|
||
|
||
**Zusätzlicher Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `recipe.metaId` | ja (für Update) | metaId des zu aktualisierenden Rezepts |
|
||
|
||
**Newline-Hinweis:** Zutaten und Anleitung müssen echte `\n`-Zeichen enthalten
|
||
(nicht die zwei-Zeichen-Sequenz `\n`). Literale Backslash-n werden vom Server
|
||
als ein Element interpretiert → kein Splitting in `ingredientsList`.
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
### `metasync` (id='recipe') – Alle Rezepte abrufen
|
||
POST https://api.familywall.com/api/metasync
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `id` | `"recipe"` (lowercase, enum-Wert) |
|
||
|
||
**Response-Struktur:**
|
||
```
|
||
a00.r.r.updatedCreated[] → Liste aller Rezepte der Familie
|
||
→ Felder identisch mit mprecipeput-Response (siehe oben)
|
||
.isRecipe → "true" oder "false" (wichtig!)
|
||
```
|
||
|
||
**isRecipe-Flag (v0.11.8):**
|
||
- `isRecipe="true"` → Echtes Rezept in der Rezeptbox (`get_recipe_box` gibt es zurück)
|
||
- `isRecipe="false"` → Freitext-Stub aus dem Essensplaner ODER altes importiertes Rezept,
|
||
das nie korrekt getaggt wurde
|
||
- `get_recipe_box` filtert strikt nach `isRecipe="true"` — konsistent mit der Family Wall App
|
||
- **Bekanntes Beispiel:** `recipe/16282169_7055369` ("Elsässer Flammkuchen") hat `isRecipe=false`
|
||
trotz vorhandener Zutaten und Anleitung. Family Wall behandelt es nicht als Rezeptbox-Eintrag.
|
||
|
||
**Hinweis:** Der Parameter `id` nimmt einen MetaIdTypeEnum-Wert, kein tatsächliches Objekt.
|
||
Nur `"recipe"` (lowercase) funktioniert – `"RECIPE"`, `"Recipe"` und andere Schreibweisen
|
||
liefern `MetaIdTypeEnum`-Fehler.
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
### `metadelete` – Rezept löschen
|
||
POST https://api.familywall.com/api/metadelete
|
||
|
||
Identisch mit dem Task-Löschen-Endpoint. Funktioniert auch für Rezepte.
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Wert |
|
||
|---|---|
|
||
| `id` | Rezept-metaId (z.B. `"recipe/23431854_10968866"`) |
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → "true" (String)
|
||
```
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
### `acccreatefamily` – Kreis erstellen
|
||
POST https://api.familywall.com/api/acccreatefamily
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `name` | ja | Kreis-Name |
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → numerische Kreis-ID als String (z.B. "23447369")
|
||
→ vollständige metaId: "family/" + id
|
||
```
|
||
|
||
**Hinweise:**
|
||
- Der Server kapitalisiert den ersten Buchstaben des Namens.
|
||
- Kreise können über die API nicht gelöscht werden (`metadelete` → "delete not supported").
|
||
- Vollständige Kreisdaten (incl. Name) via `famlistfamily` im gleichen Session-Call abrufbar.
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
### `accinvite` – Mitglied einladen
|
||
POST https://api.familywall.com/api/accinvite
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `familyId` | ja | Kreis-metaId (z.B. `"family/23447369"`) |
|
||
| `identifier` | ja | E-Mail-Adresse des Eingeladenen |
|
||
| `role` | ja | `"Unknown"` (einziger verifizierter Enum-Wert für `FamilyRoleTypeEnum`) |
|
||
| `firstname` | ja | Vorname des Eingeladenen |
|
||
|
||
**Einschränkung:** Der Endpoint funktioniert nur für Personen, die noch kein
|
||
Family Wall-Konto haben. Bei bestehenden Accounts antwortet der Server mit:
|
||
`"This api is now only used to create and invite an account with login only."`
|
||
|
||
**Fehler-Struktur** (HTTP 200, aber Fehler unter `a00.ex`):**
|
||
```json
|
||
{"a00": {"ex": {"ex": {"FiZClassId": "17", "message": "..."}}}}
|
||
```
|
||
→ `fw_client.py` prüft `a00.ex` und wirft `FamilyWallError`.
|
||
|
||
**Response (Erfolg):**
|
||
```
|
||
a00.r.r → Einladungsobjekt (Struktur unbekannt, da nicht testbar mit Test-Account)
|
||
```
|
||
|
||
**Bekannte Enum-Werte für `role`:**
|
||
- `"Unknown"` ✓ verifiziert
|
||
- `"Parent"`, `"Child"` → `FamilyRoleTypeEnum` decode error
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||
|
||
### `accupdatefamily` – Kreis umbenennen
|
||
POST https://api.familywall.com/api/accupdatefamily
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `name` | ja | Neuer Kreis-Name |
|
||
| `scope` | nein | Kreis-metaId z.B. `"family/23449644"` (ohne scope → primärer Kreis) |
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → vollständiges Kreis-Objekt
|
||
.metaId → Kreis-metaId (z.B. "family/23449644")
|
||
.name → (aktualisierter) Kreis-Name (erster Buchstabe wird kapitalisiert)
|
||
.family_id → numerische Kreis-ID (ohne "family/"-Prefix)
|
||
.members[] → Mitglieder-Liste
|
||
```
|
||
|
||
**Hinweise:**
|
||
- Ohne `scope`: aktualisiert den PRIMARY Kreis des Accounts.
|
||
- Mit `scope=family/XXXX`: aktualisiert den angegebenen Kreis (primär oder sekundär).
|
||
- Der Server kapitalisiert immer den ersten Buchstaben des Namens.
|
||
- Nur der Name ist über die API änderbar (kein Bild, keine Farbe).
|
||
|
||
**Vorherige Dokumentation korrigiert:** Frühere SPEC-Einträge vermerkten,
|
||
dass `id`/`familyId` ignoriert werden. Mit `scope=` funktioniert die
|
||
Zielauswahl korrekt für beliebige Kreise.
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1 (scope= auf sekundärem Kreis)
|
||
|
||
### `adminwipefamily` – Kreis löschen
|
||
|
||
POST https://api.familywall.com/api/adminwipefamily
|
||
|
||
Löscht einen Kreis und alle zugehörigen Inhalte (Listen, Tasks, Rezepte, Wall-Posts).
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `scope` | ja | Kreis-metaId (z.B. `family/23447378`) |
|
||
|
||
**Response (Erfolg):**
|
||
|
||
```json
|
||
{ "a00": { "r": { "r": "true" } } }
|
||
```
|
||
|
||
**Hinweise:**
|
||
- Löscht unwiderruflich alle Inhalte des Kreises.
|
||
- Der primäre Kreis (`isFirstFamily="true"` in `famlistfamily`) kann NICHT gelöscht werden.
|
||
- MCP-Server prüft `isFirstFamily` vor dem Löschen via `famlistfamily` im gleichen Session-Call.
|
||
|
||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1 (family/23447371 erfolgreich gelöscht)
|
||
|
||
## Meal Planner API
|
||
|
||
Premium-Feature. Endpoints extrahiert aus JS-Bundle `startupmodule.js?vfw=2026-04-03T140539`.
|
||
Browser-Analyse blockiert durch Service Worker. Response-Struktur wird nach erstem Deploy
|
||
mit `FW_DEBUG=1` verifiziert.
|
||
|
||
### `mplistinterval` – Essensplan für Datumsbereich abrufen
|
||
POST https://api.familywall.com/api/mplistinterval
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `from` | ja | Start-Datum ISO 8601 (z.B. `"2026-04-13"`) |
|
||
| `to` | ja | End-Datum ISO 8601 (z.B. `"2026-04-19"`) |
|
||
|
||
**Response-Struktur:**
|
||
```
|
||
a00.r.r
|
||
.from → angefragtes Start-Datum (z.B. "2026-04-13")
|
||
.to → angefragtes End-Datum (z.B. "2026-04-19")
|
||
.list[] → geplante Mahlzeiten (dish-Objekte)
|
||
.metaId → "dish/<family_num>_<dish_num>"
|
||
.date → Datum der Mahlzeit (z.B. "2026-04-17")
|
||
.type → Mahlzeiten-Typ (s.u.)
|
||
.name → Anzeigename (z.B. "Biga Pizzateig")
|
||
.recipeId → verknüpfte Rezept-metaId oder fehlt wenn freier Text
|
||
.familyId → Kreis-metaId
|
||
.accountId → Ersteller-accountId
|
||
.sortingIndex → Sortierung (numerischer Timestamp als String)
|
||
.rights.canUpdate → "true" wenn bearbeitbar
|
||
.rights.canDelete → "true" wenn löschbar
|
||
.recipeList[] → Rezept-Objekte der verknüpften Gerichte
|
||
.metaId → Rezept-ID (z.B. "recipe/16282169_7932720")
|
||
.isRecipe → "true" = echtes Rezept aus der Rezeptbox (hat Zutaten etc.)
|
||
"false" = Freitext-Stub (nur Name, keine Zutaten)
|
||
.mealList[] → Freitext-Notizen (meal-Objekte)
|
||
.metaId → "meal/<family_num>_<meal_num>"
|
||
.date → Datum der Mahlzeit (z.B. "2026-04-17")
|
||
.type → Mahlzeiten-Typ (BREAKFAST/LUNCH/SNACK/DINNER)
|
||
.note → Freitext-Notiz (z.B. "Test")
|
||
.serves → Portionen als String (z.B. "1")
|
||
.rights.canUpdate → "true" wenn bearbeitbar
|
||
.rights.canDelete → "true" wenn löschbar
|
||
```
|
||
|
||
**Mahlzeiten-Typen:**
|
||
- `BREAKFAST` – Frühstück
|
||
- `LUNCH` – Mittagessen
|
||
- `SNACK` – Snack
|
||
- `DINNER` – Abendessen
|
||
|
||
**Hinweis:** `recipeList` wird vom MCP-Tool nicht zurückgegeben.
|
||
Rezept-Details bei Bedarf separat via `get_recipe` abrufen.
|
||
`list[]` (dish) und `mealList[]` (meal) werden gemergt und nach Datum + Typ sortiert zurückgegeben.
|
||
|
||
**Verifiziert am:** 2026-04-17 via FW_DEBUG=1
|
||
|
||
### `mpcreateByRecipeId` – Rezept in Essensplan eintragen
|
||
POST https://api.familywall.com/api/mpcreateByRecipeId
|
||
|
||
**Body-Parameter (verifiziert aus JS-Bundle startupmodule.js):**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `recipeId` | ja | Rezept-metaId (z.B. `"recipe/16282169_7932720"`) |
|
||
| `date` | ja | Ziel-Datum ISO 8601 (z.B. `"2026-04-20"`) |
|
||
| `type` | ja | Mahlzeiten-Typ: `BREAKFAST`, `LUNCH`, `SNACK`, `DINNER` |
|
||
| `clientOpId` | nein | Optionale Client-seitige Idempotenz-ID (wird weggelassen) |
|
||
|
||
**Response-Struktur:**
|
||
```
|
||
a00.r.r → vollständiges dish-Objekt
|
||
.metaId → neue Dish-ID (z.B. "dish/16282169_20009811")
|
||
.date → Datum (z.B. "2026-04-18")
|
||
.type → Mahlzeiten-Typ (BREAKFAST/LUNCH/SNACK/DINNER)
|
||
.name → Rezeptname
|
||
.recipeId → verknüpfte Rezept-metaId
|
||
.familyId → Kreis-metaId
|
||
.accountId → Ersteller-accountId
|
||
.sortingIndex → Sortierung (numerischer Timestamp als String)
|
||
.rights.canUpdate → "true"
|
||
.rights.canDelete → "true"
|
||
a00.cn → "mpcreateByRecipeId" (Endpoint-Echo)
|
||
```
|
||
|
||
**Verifiziert am:** 2026-04-17 via FW_DEBUG=1
|
||
|
||
### `mpcreate` – Freitext-Mahlzeit in Essensplan eintragen
|
||
POST https://api.familywall.com/api/mpcreate
|
||
|
||
**Body-Parameter (verifiziert aus JS-Bundle startupmodule.js):**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `name` | ja | Anzeigename der Mahlzeit (z.B. `"Pfannkuchen"`) |
|
||
| `date` | ja | Ziel-Datum ISO 8601 (z.B. `"2026-04-20"`) |
|
||
| `type` | ja | Mahlzeiten-Typ: `BREAKFAST`, `LUNCH`, `SNACK`, `DINNER` |
|
||
| `clientOpId` | nein | Optionale Client-seitige Idempotenz-ID (wird weggelassen) |
|
||
|
||
**Response-Struktur:**
|
||
```
|
||
a00.r.r → Array (⚠️ nicht Objekt wie bei mpcreateByRecipeId!)
|
||
[0] → neues dish-Objekt
|
||
.metaId → neue Dish-ID (z.B. "dish/16282169_20010208")
|
||
.date → Datum (z.B. "2026-04-20")
|
||
.type → Mahlzeiten-Typ (BREAKFAST/LUNCH/SNACK/DINNER)
|
||
.name → Freitext-Name
|
||
.recipeId → vom Server generierte Stub-Rezept-ID (isRecipe="false")
|
||
.rights.canUpdate → "true"
|
||
.rights.canDelete → "true"
|
||
a00.cn → "mpcreate" (Endpoint-Echo)
|
||
```
|
||
|
||
**Hinweis:** Der Server legt intern ein Stub-Rezept (`isRecipe="false"`) an und
|
||
verknüpft es mit dem Dish-Objekt. `is_from_recipe_box` ist daher `false`.
|
||
|
||
**Verifiziert am:** 2026-04-17 via FW_DEBUG=1
|
||
|
||
### `mpmealput` – Meal-Notiz erstellen/aktualisieren
|
||
POST https://api.familywall.com/api/mpmealput
|
||
|
||
**Body-Parameter (verifiziert aus JS-Bundle, Encoder `UI(O)`):**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `date` | ja | Ziel-Datum ISO 8601 (z.B. `"2026-04-20"`) |
|
||
| `type` | ja | Mahlzeiten-Typ: `BREAKFAST`, `LUNCH`, `SNACK`, `DINNER` |
|
||
| `note` | nein | Freitext-Notiz (z.B. `"Bitte ohne Zwiebeln"`) |
|
||
| `serves` | nein | Portionen als String (z.B. `"4"`) — Vt = int→string Konverter |
|
||
| `metaId` | nein | metaId eines bestehenden meal/-Objekts → Update; ohne → Create |
|
||
|
||
**Hinweis:** Ohne `metaId` wird ein neues `meal/`-Objekt erstellt.
|
||
Mit `metaId` wird ein bestehendes aktualisiert (Update, noch nicht implementiert).
|
||
|
||
**Response-Struktur:**
|
||
```
|
||
a00.r.r → meal-Objekt (Objekt, nicht Array)
|
||
.metaId → neue/aktualisierte Meal-ID (z.B. "meal/16282169_...")
|
||
.date → Datum
|
||
.type → Mahlzeiten-Typ
|
||
.note → Freitext-Notiz
|
||
.serves → Portionen als String (z.B. "1")
|
||
.familyId → Kreis-metaId
|
||
.accountId → Ersteller-accountId
|
||
.rights.canUpdate → "true"
|
||
.rights.canDelete → "true"
|
||
```
|
||
|
||
**Verifiziert am:** 2026-04-17 (Parameter aus JS-Bundle; Response-Struktur aus `Sg`-Klasse im Bundle)
|
||
|
||
### Weitere Meal Planner Endpoints (nicht implementiert)
|
||
|
||
| Endpoint | Parameter | Bedeutung |
|
||
|---|---|---|
|
||
| `mpmove` | `metaId`, `date`, `type`, `clientOpId` | Mahlzeit zu anderem Datum/Typ verschieben |
|
||
| `mpdelete` | `metaId` | Mahlzeit löschen |
|
||
| `mpsettings` | – | Einstellungen lesen |
|
||
|
||
**Quelle:** JS-Bundle-Analyse (startupmodule.js).
|
||
**Status:** Endpunkte bekannt, Response-Struktur unbekannt.
|
||
|
||
### Recipe Categories API Details
|
||
|
||
Recipe categories are managed via `mprecipeput` endpoint. Each recipe can have zero or more
|
||
categories assigned via `recipe.recipeCategoryIdList` (sent multiple times for each category).
|
||
|
||
**Response structure** (in `mprecipeput` response):
|
||
|
||
```
|
||
a00.r.r
|
||
.recipeCategoryIdList[] → List of assigned category IDs (e.g. ["category/23431854_2"])
|
||
.recipeCategories[] → List of system names (e.g. ["KIDS_LOVE"])
|
||
```
|
||
|
||
**Category IDs** have format `category/<family_id>_<index>`. Categories are family-wide.
|
||
Standard categories (_2 to _6) are always available and derived from the family's metaId.
|
||
No API endpoint needed – IDs are constructed as `category/<familyId>_<N>`.
|
||
Premium accounts may have additional categories beyond the 5 available in free tier.
|
||
|
||
### `metamood` – Rezept-Reaktion (Stern setzen)
|
||
POST https://api.familywall.com/api/metamood
|
||
|
||
**Body-Parameter:**
|
||
|
||
| Parameter | Pflicht | Wert |
|
||
|---|---|---|
|
||
| `id` | ja | Rezept-metaId ⚠️ nicht `metaId`! |
|
||
| `moodType` | nein | `"STAR"`, `"LIKE"`, `"BEST_MOMENT"`, ... (Wert wird angenommen aber ignoriert) |
|
||
|
||
**Response:**
|
||
```
|
||
a00.r.r → Rezept-Objekt (FiZClassId: "8004")
|
||
.metaId → Rezept-ID
|
||
.isFavorite → aktueller Wert (unverändert)
|
||
.moodStarShortcut → aktueller Wert (unverändert)
|
||
.moodMap → aktueller Wert (unverändert)
|
||
```
|
||
|
||
**Bekannte Einschränkungen:**
|
||
- Eigene Rezepte können nicht reagiert werden (Self-Reaction-Restriction, analog zu `wallmood`)
|
||
- Der Endpoint akzeptiert Requests (HTTP 200, kein Fehler), verändert aber bei eigenen Rezepten keinen State
|
||
- `FiZClassId: "8004"` im Response deutet auf "verarbeitet aber nicht geändert" hin
|
||
- Möglicherweise funktioniert der Endpoint für Rezepte anderer Familienmitglieder (nicht testbar mit Einzel-Account)
|
||
|
||
**Entdeckt:** 2026-04-17 via FW_DEBUG=1 Probing
|
||
|
||
### `mpstar` – Rezept als Favorit markieren (Service Worker blockiert)
|
||
POST https://api.familywall.com/api/mpstar
|
||
|
||
**Status:** Nicht direkt zugänglich. Der Server antwortet mit `"The call mpstar is not registered"`.
|
||
Der Browser-Aufruf `data-fw-onclick="mpstar"` wird vom Service Worker abgefangen und transformiert,
|
||
bevor er den Server erreicht. Die Transformation ist ohne Service-Worker-Analyse nicht bekannt.
|
||
|
||
**Hypothesen zum echten Endpoint:**
|
||
- Könnte eine transformierte Form von `metamood` sein (mit Verschlüsselung/Token)
|
||
- Könnte ein separater "Lesezeichen"-Endpoint sein der `isFavorite` setzt (vs. `moodMap`-Stern)
|
||
- Könnte Premium-only sein
|
||
|
||
**Zum Entsperren nötig:**
|
||
- Service-Worker-Quellcode analysieren (DevTools Application > Service Workers > Source)
|
||
- ODER: `FW_DEBUG=1` auf einem echten Multi-User-Account mit Rezepten anderer Mitglieder testen
|
||
|
||
**Entdeckt:** 2026-04-17 via FW_DEBUG=1 Probing (ausgiebige Endpoint-Suche)
|
||
|
||
## Offene Punkte
|
||
|
||
- `mpstar` / `isFavorite` für Rezepte (Service Worker blockiert Analyse, siehe oben)
|
||
- Erinnerungen (reminder) – nur Premium-Account
|
||
- Wiederholungen (repeat) – nur Premium-Account
|
||
- Rezept-Kategorien-Listing-Endpoint (derzeit keine API, müssen aus Rezepten extrahiert werden)
|
||
- mpadditemtolist (nicht nötig – Family Wall kann das nativ über die Web-App)
|
||
- Einladung bestehender FamilyWall-Nutzer (accinvite nur für neue Accounts)
|