mitai-jinkendo/.claude/docs/technical/API_REFERENCE.md
Lars 7940dc7560 docs: Struktur .claude/docs versionieren, working/, Gitea-Index, Regeln
- .gitignore: .claude/docs, rules, commands tracken; settings.local weiter ignorieren
- DOCUMENTATION.md: verbindliche Ablage functional/technical/working/issues
- .claude/README.md: Agent-Einstieg; GITEA_ISSUES_INDEX aus MCP (Stand 2026-04-08)
- Arbeitspapiere von docs/ nach .claude/docs/working/ verschoben
- docs/MEMBERSHIP_SYSTEM.md als Stub; kanonisch technical/MEMBERSHIP_SYSTEM.md
- CLAUDE.md Pflichtlektüre und Links angepasst; docs/README.md vereinfacht

Made-with: Cursor
2026-04-08 13:01:49 +02:00

26 KiB
Raw Blame History

API-Referenz

Basis-URLs

Umgebung URL
Production https://mitai.jinkendo.de/api
Development https://dev.mitai.jinkendo.de/api
Local http://localhost:8000/api (Backend direkt)

Authentifizierung

Alle geschützten Endpoints benötigen einen Auth-Token im Header:

GET /api/weight
X-Auth-Token: jT9z3xK...pQ2vL

Token-Beschaffung: POST /api/auth/login{"token": "..."}

Storage: localStorage.bodytrack_token (Frontend)


Fehler-Codes

Status Bedeutung Beispiel
200 Erfolg {"data": [...]}
201 Erstellt {"id": "uuid", ...}
400 Bad Request {"detail": "Ungültige Eingabe"}
401 Unauthorized {"detail": "Nicht eingeloggt"}
403 Forbidden {"detail": "Nur für Admins"} oder {"detail": "Feature-Limit erreicht"}
404 Not Found {"detail": "Eintrag nicht gefunden"}
429 Too Many Requests {"detail": "Rate limit exceeded: 5 per 1 minute"}
500 Server Error {"detail": "Interner Fehler"}

Standard-Fehler-Format:

{
  "detail": "Fehlermeldung"
}

Rate Limits

Endpoint Limit Grund
POST /api/auth/login 5/minute Brute-Force-Schutz
POST /api/auth/register 3/hour Spam-Prevention
POST /api/auth/forgot-password 3/minute E-Mail-Flooding-Schutz
POST /api/auth/resend-verification 3/hour E-Mail-Flooding-Schutz

Andere Endpoints: Keine Rate-Limits (Feature-Limits via Membership-System)


Endpoints nach Modul

1. Auth (/api/auth/*)

Methode Pfad Auth Parameter Response Beschreibung
POST /auth/login {email, password} {token, profile_id, name, role, expires_at} Login mit E-Mail + Passwort
POST /auth/logout {ok: true} Logout (löscht Session)
GET /auth/me {id, name, email, role, tier, ...} Aktuelles Profil abrufen
GET /auth/status {status: "ok", version: "v9b"} Health Check
PUT /auth/pin {pin} {ok: true} PIN/Passwort ändern
POST /auth/forgot-password {email} {ok: true, message} Passwort-Reset anfordern
POST /auth/reset-password {token, new_password} {ok: true, message} Passwort-Reset bestätigen
POST /auth/register {name, email, password} {ok: true, message} Selbst-Registrierung
GET /auth/verify/{token} {ok: true, token, profile} E-Mail-Verifizierung
POST /auth/resend-verification {email} {ok: true, message} Verifizierungs-E-Mail erneut senden

Rate Limits: Siehe Tabelle oben


2. Profiles (/api/profiles/*, /api/profile)

Methode Pfad Auth Parameter Response Beschreibung
GET /profile {id, name, email, role, tier, ...} Aktives Profil
PUT /profile {name?, sex?, dob?, height?, goal_weight?, goal_bf_pct?, avatar_color?} {ok: true} Profil aktualisieren
GET /profiles [{id, name, ...}, ...] Alle Profile (Multi-User, derzeit nicht genutzt)
POST /profiles {name, ...} {id, ...} Profil erstellen (Multi-User)
PUT /profiles/{id} {name?, ...} {ok: true} Profil bearbeiten
DELETE /profiles/{id} {ok: true} Profil löschen

3. Weight (/api/weight/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /weight ?limit=365 [{id, date, weight, note, source, created}, ...] Gewichtseinträge abrufen
POST /weight {date, weight, note?} {id, date, weight, ...} Gewicht eintragen (Upsert)
PUT /weight/{id} {date, weight, note?} {ok: true} Eintrag bearbeiten
DELETE /weight/{id} {ok: true} Eintrag löschen
GET /weight/stats {latest, avg_7d, avg_30d, delta_7d, delta_30d, trend} Gewichts-Statistiken

Feature-Limits: weight_entries (v9c Phase 4: Enforcement aktiv)


4. Circumferences (/api/circumferences/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /circumferences ?limit=100 [{id, date, c_neck, c_chest, c_waist, c_belly, c_hip, c_thigh, c_calf, c_arm, notes, photo_id}, ...] Umfangsmessungen
POST /circumferences {date, c_neck?, c_chest?, ...} {id, ...} Messung eintragen (Upsert)
PUT /circumferences/{id} {date, ...} {ok: true} Bearbeiten
DELETE /circumferences/{id} {ok: true} Löschen

Feature-Limits: circumference_entries


5. Caliper (/api/caliper/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /caliper ?limit=100 [{id, date, sf_method, sf_chest, sf_abdomen, ..., body_fat_pct, lean_mass, fat_mass, notes}, ...] Hautfaltenmessungen
POST /caliper {date, sf_method, sf_chest?, ...} {id, body_fat_pct, ...} Messung eintragen (berechnet KF% automatisch)
PUT /caliper/{id} {date, ...} {ok: true} Bearbeiten
DELETE /caliper/{id} {ok: true} Löschen

Methoden: jackson3, jackson7, durnin, parrillo

Feature-Limits: caliper_entries


6. Activity (/api/activity/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /activity ?limit=200 [{id, date, start_time, end_time, activity_type, duration_min, kcal_active, hr_avg, hr_max, distance_km, rpe, source, notes}, ...] Aktivitäten
POST /activity {date, activity_type, duration_min, ...} {id, ...} Aktivität erstellen
PUT /activity/{id} {date, ...} {ok: true} Bearbeiten
DELETE /activity/{id} {ok: true} Löschen
GET /activity/stats {total_activities, total_kcal, avg_duration, ...} Statistiken
GET /activity/uncategorized [{activity_type, count}, ...] Unkategorisierte Aktivitäten
POST /activity/bulk-categorize [{activity_type, training_type_id}, ...] {ok: true, updated_count} Bulk-Kategorisierung (lernendes System)
POST /activity/import-csv FormData(file) {imported, skipped, failed, errors} Apple Health CSV-Import

Feature-Limits: activity_entries


7. Nutrition (/api/nutrition/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /nutrition ?limit=365 [{id, date, kcal, protein_g, fat_g, carbs_g, source}, ...] Ernährungsdaten
GET /nutrition/by-date/{date} {id, date, kcal, ...} oder null Eintrag für bestimmtes Datum
POST /nutrition ?date=YYYY-MM-DD&kcal=2000&protein_g=150&fat_g=70&carbs_g=200 {id, ...} Eintrag erstellen (Upsert)
PUT /nutrition/{id} ?kcal=2000&... {ok: true} Bearbeiten
DELETE /nutrition/{id} {ok: true} Löschen
GET /nutrition/correlations {weight_vs_kcal: [...], bf_vs_protein: [...]} Korrelationen
GET /nutrition/weekly ?weeks=16 [{week, year, avg_kcal, avg_protein, ...}, ...] Wochendaten
GET /nutrition/import-history [{date, count}, ...] Import-Historie
POST /nutrition/import-csv FormData(file) {imported, skipped, failed, errors} CSV-Import (FDDB, MyFitnessPal)

Feature-Limits: nutrition_entries, data_import


8. Photos (/api/photos/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /photos [{id, date, path, created}, ...] Alle Fotos
GET /photos/{id} ?token=... (optional) Binary (JPEG) Foto abrufen (Token für tag)
POST /photos FormData(file, date?) {id, path, date} Foto hochladen
DELETE /photos/{id} {ok: true} Foto löschen

Feature-Limits: photos

Hinweis: Token-Parameter für GET /photos/{id} erlaubt Zugriff via <img> tag (ohne Header-Support)


9. AI Insights (/api/insights/*, /api/ai/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /insights [{id, scope, content, created}, ...] Alle Insights
GET /insights/latest {slug: content, ...} Neueste Insights pro Scope
POST /insights/trend {content, usage} Trend-Analyse (Gewicht)
POST /insights/run/{slug} {content, usage} Einzelnen Prompt ausführen
POST /insights/pipeline {content, usage} 3-stufige Pipeline

Feature-Limits: ai_calls (pro Aufruf), ai_pipeline (für Pipeline)

Prompts: Konfigurierbar via /api/prompts


10. AI Prompts (/api/prompts/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /prompts [{id, slug, name, description, template, active, sort_order}, ...] Alle Prompts
PUT /prompts/{id} 🔒 Admin {name?, description?, template?, active?, sort_order?} {ok: true} Prompt bearbeiten

Standard-Prompts:

  • weight-trend Gewichts-Trend-Analyse
  • nutrition-analysis Ernährungs-Auswertung
  • training-plan Trainings-Empfehlungen
  • body-composition Körperzusammensetzung
  • progress-summary Fortschritts-Zusammenfassung
  • pipeline Master-Schalter für Pipeline

11. Admin (/api/admin/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /admin/profiles 🔒 Admin [{id, name, email, role, tier, ...}, ...] Alle Profile
POST /admin/profiles 🔒 Admin {name, email, password, role?} {id, ...} Profil erstellen
DELETE /admin/profiles/{id} 🔒 Admin {ok: true} Profil löschen
PUT /admin/profiles/{id}/permissions 🔒 Admin {ai_enabled?, ai_limit_day?, export_enabled?} {ok: true} Permissions setzen

12. Stats (/api/stats)

Methode Pfad Auth Parameter Response Beschreibung
GET /stats {weight: {...}, nutrition: {...}, activity: {...}, body_comp: {...}} Dashboard-Stats

Response-Struktur:

{
  "weight": {
    "latest": 75.5,
    "avg_7d": 75.8,
    "avg_30d": 76.2,
    "delta_7d": -0.3,
    "delta_30d": -0.7
  },
  "nutrition": {
    "avg_kcal_7d": 2100,
    "avg_protein_7d": 150,
    "days_logged_7d": 6
  },
  "activity": {
    "total_activities_7d": 5,
    "total_kcal_7d": 2500,
    "avg_duration_7d": 45
  },
  "body_comp": {
    "latest_bf_pct": 12.5,
    "latest_lean_mass": 65.8
  }
}

13. Export (/api/export/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /export/csv Binary (CSV) Alle Daten als CSV
GET /export/json Binary (JSON) Alle Daten als JSON
GET /export/zip Binary (ZIP) Alle Daten + Fotos als ZIP

Feature-Limits: data_export

Dateiname: mitai-export-YYYY-MM-DD.[csv|json|zip]

ZIP-Struktur:

mitai-export-2026-03-23.zip
├── data.json
├── photos/
│   ├── photo_uuid1.jpg
│   └── photo_uuid2.jpg
└── README.txt

14. Import (/api/import/*)

Methode Pfad Auth Parameter Response Beschreibung
POST /import/backup FormData(file) {imported: {...}, skipped: {...}, failed: {...}} JSON-Backup importieren

Feature-Limits: data_import

Format: JSON-Export von /export/json

Hinweis: Überschreibt keine existierenden Einträge (Upsert-Logik)


Subscription System (v9c)

15. Subscription (/api/subscription/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /subscription/me {tier, tier_expires_at, trial_ends_at, invited_by, ...} Abo-Status
GET /subscription/usage {feature_id: {used, limit, remaining, allowed}, ...} Feature-Usage
GET /subscription/limits {feature_id: limit, ...} Feature-Limits für aktuelles Tier

16. Coupons (/api/coupons/*)

Methode Pfad Auth Parameter Response Beschreibung
POST /coupons/redeem {code} {ok: true, tier_id, valid_days, message} Coupon einlösen
GET /coupons 🔒 Admin [{id, code, tier_id, valid_days, max_uses, ...}, ...] Alle Coupons
POST /coupons 🔒 Admin {code, tier_id, valid_days, max_uses?} {id, ...} Coupon erstellen
PUT /coupons/{id} 🔒 Admin {...} {ok: true} Bearbeiten
DELETE /coupons/{id} 🔒 Admin {ok: true} Löschen
GET /coupons/{id}/redemptions 🔒 Admin [{profile_id, redeemed_at}, ...] Einlösungen

17. Features (/api/features/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /features [{id, name, description, limit_type, reset_period, default_limit, active}, ...] Alle Features
GET /features/usage [{feature_id, feature_name, limit, used, remaining, allowed, reason}, ...] Feature-Usage für aktuellen User
POST /features 🔒 Admin {id, name, description, limit_type, reset_period, default_limit} {id, ...} Feature erstellen
PUT /features/{id} 🔒 Admin {...} {ok: true} Bearbeiten
DELETE /features/{id} 🔒 Admin {ok: true} Löschen

Limit-Types: count (zählbar), boolean (on/off)

Reset-Periods: never, daily, monthly


18. Tiers (/api/tiers/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /tiers 🔒 Admin [{id, name, description, price_monthly, active}, ...] Alle Tiers
POST /tiers 🔒 Admin {id, name, description, price_monthly} {id, ...} Tier erstellen
PUT /tiers/{id} 🔒 Admin {...} {ok: true} Bearbeiten
DELETE /tiers/{id} 🔒 Admin {ok: true} Löschen

Standard-Tiers: free, basic, premium, selfhosted


19. Tier Limits (/api/tier-limits/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /tier-limits 🔒 Admin {tiers: [...], features: [...], matrix: {...}} Tier-Limits-Matrix
PUT /tier-limits 🔒 Admin {tier_id, feature_id, limit_value} {ok: true} Limit setzen
PUT /tier-limits/batch 🔒 Admin {updates: [{tier_id, feature_id, limit_value}, ...]} {ok: true} Batch-Update

Matrix-Format:

{
  "tiers": ["free", "basic", "premium", "selfhosted"],
  "features": [
    {"id": "weight_entries", "name": "Gewichtseinträge", ...},
    ...
  ],
  "matrix": {
    "weight_entries": {
      "free": 100,
      "basic": 1000,
      "premium": null,
      "selfhosted": null
    },
    ...
  }
}

20. User Restrictions (/api/user-restrictions/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /user-restrictions 🔒 Admin ?profile_id=... [{id, profile_id, feature_id, limit_value}, ...] User-spezifische Limits
POST /user-restrictions 🔒 Admin {profile_id, feature_id, limit_value} {id, ...} Limit setzen
PUT /user-restrictions/{id} 🔒 Admin {limit_value} {ok: true} Bearbeiten
DELETE /user-restrictions/{id} 🔒 Admin {ok: true} Löschen

Verwendung: Individuelle Limits überschreiben Tier-Limits


21. Access Grants (/api/access-grants/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /access-grants 🔒 Admin ?profile_id=...&active_only=true [{id, profile_id, tier_id, valid_from, valid_until, source, is_active}, ...] Zeitlich begrenzte Tier-Zugriffe
POST /access-grants 🔒 Admin {profile_id, tier_id, valid_from, valid_until, source?} {id, ...} Grant erstellen
PUT /access-grants/{id} 🔒 Admin {...} {ok: true} Bearbeiten
DELETE /access-grants/{id} 🔒 Admin {ok: true} Revoke

Quellen: coupon, trial, manual, gift


Training Types (v9d)

22. Training Types (/api/training-types/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /training-types {category: [{id, name, category, color, icon}, ...], ...} Gruppiert nach Kategorie
GET /training-types/flat [{id, name, category, ...}, ...] Flache Liste
GET /training-types/categories [{id, name, icon, color}, ...] Kategorien-Metadaten

Kategorien: Kraft, Cardio, Flexibilität, Spiel & Sport, Alltag & Bewegung, Outdoor & Natur, Geist & Meditation


23. Admin Training Types (/api/admin/training-types/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /admin/training-types 🔒 Admin [{id, name, category, color, icon, abilities}, ...] Alle Typen (inkl. abilities JSONB)
GET /admin/training-types/{id} 🔒 Admin {id, name, ...} Einzelner Typ
POST /admin/training-types 🔒 Admin {name, category, color?, icon?} {id, ...} Typ erstellen
PUT /admin/training-types/{id} 🔒 Admin {name?, category?, color?, icon?, abilities?} {ok: true} Bearbeiten
DELETE /admin/training-types/{id} 🔒 Admin {ok: true} Löschen
GET /admin/training-types/taxonomy/abilities 🔒 Admin {categories: [...], abilities: [...]} Abilities-Taxonomie (v9f)

24. Activity Mappings (/api/admin/activity-mappings/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /admin/activity-mappings 🔒 Admin ?profile_id=...&global_only=true [{id, activity_type, training_type_id, profile_id, is_global}, ...] Lernendes Mapping-System
GET /admin/activity-mappings/{id} 🔒 Admin {id, ...} Einzelnes Mapping
POST /admin/activity-mappings 🔒 Admin {activity_type, training_type_id, profile_id?, is_global?} {id, ...} Mapping erstellen
PUT /admin/activity-mappings/{id} 🔒 Admin {training_type_id?, is_global?} {ok: true} Bearbeiten
DELETE /admin/activity-mappings/{id} 🔒 Admin {ok: true} Löschen
GET /admin/activity-mappings/stats/coverage 🔒 Admin {total_activities, mapped, unmapped, coverage_pct} Mapping-Coverage

Auto-Learning: Bulk-Kategorisierung in ActivityPage speichert neue Mappings automatisch


25. Training Profiles (/api/evaluation/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /evaluation/parameters 🔒 Admin {categories: [...], abilities: [...]} Training-Parameter-Taxonomie
POST /evaluation/batch 🔒 Admin {evaluated, failed, errors} Batch-Evaluierung aller Aktivitäten (v9d #15)

Sleep & Vitals (v9d Phase 2)

26. Sleep (/api/sleep/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /sleep ?limit=90 [{id, date, bedtime, wakeup, duration_min, quality, sleep_segments, notes, source}, ...] Schlaf-Einträge
GET /sleep/by-date/{date} {id, date, ...} oder null Eintrag für Datum
POST /sleep {date, bedtime, wakeup, duration_min, quality?, sleep_segments?, notes?} {id, ...} Eintrag erstellen (Upsert)
PUT /sleep/{id} {...} {ok: true} Bearbeiten
DELETE /sleep/{id} {ok: true} Löschen
GET /sleep/stats ?days=7 {avg_duration, avg_quality, total_deep, total_rem, ...} Stats
GET /sleep/debt ?days=14 {sleep_debt_min, avg_duration, target_duration} Schlafschuld
GET /sleep/trend ?days=30 [{date, duration_min, quality}, ...] Trend
GET /sleep/phases ?days=30 [{date, deep_min, rem_min, light_min, awake_min}, ...] Schlafphasen
POST /sleep/import/apple-health FormData(file) {imported, skipped, failed, errors} Apple Health CSV-Import

sleep_segments Format (JSONB):

[
  {"phase": "deep", "start": "23:30", "end": "01:15"},
  {"phase": "rem", "start": "01:15", "end": "02:45"},
  {"phase": "light", "start": "02:45", "end": "06:00"},
  {"phase": "awake", "start": "06:00", "end": "06:15"}
]

27. Rest Days (/api/rest-days/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /rest-days ?limit=90 [{id, date, rest_type, reason, notes}, ...] Ruhetage
GET /rest-days/{id} {id, ...} Einzelner Eintrag
POST /rest-days {date, rest_type, reason?, notes?} {id, ...} Ruhetag eintragen
PUT /rest-days/{id} {...} {ok: true} Bearbeiten
DELETE /rest-days/{id} {ok: true} Löschen
GET /rest-days/stats ?weeks=4 {total_rest_days, kraft_days, cardio_days, entspannung_days} Stats
POST /rest-days/validate-activity {date, activity_type} {conflicts: [{rest_type, reason}, ...]} Validierung gegen Ruhetage

rest_type: kraft, cardio, entspannung (Multi-Dimensional Rest)


28. Vitals Baseline (/api/vitals/baseline/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /vitals/baseline ?limit=90 [{id, date, resting_hr, hrv, vo2_max, spo2, respiratory_rate, notes}, ...] Morgenmessungen
GET /vitals/baseline/by-date/{date} {id, ...} oder null Eintrag für Datum
POST /vitals/baseline {date, resting_hr?, hrv?, vo2_max?, spo2?, respiratory_rate?, notes?} {id, ...} Eintrag erstellen (Upsert)
PUT /vitals/baseline/{id} {...} {ok: true} Bearbeiten
DELETE /vitals/baseline/{id} {ok: true} Löschen
GET /vitals/baseline/stats ?days=30 {avg_resting_hr, avg_hrv, trend_resting_hr, trend_hrv} Stats
POST /vitals/baseline/import/apple-health FormData(file) {imported, skipped, failed, errors} Apple Health CSV-Import

Messung: 1x täglich, morgens nüchtern


29. Blood Pressure (/api/blood-pressure/*)

Methode Pfad Auth Parameter Response Beschreibung
GET /blood-pressure ?limit=90 [{id, date, time, systolic, diastolic, pulse, context, irregular_heartbeat, afib_warning, notes}, ...] Blutdruck-Messungen
GET /blood-pressure/by-date/{date} [{id, time, ...}, ...] Alle Messungen für Datum
POST /blood-pressure {date, time, systolic, diastolic, pulse?, context?, irregular_heartbeat?, afib_warning?, notes?} {id, ...} Messung eintragen
PUT /blood-pressure/{id} {...} {ok: true} Bearbeiten
DELETE /blood-pressure/{id} {ok: true} Löschen
GET /blood-pressure/stats ?days=30 {avg_systolic, avg_diastolic, avg_pulse, classification, trend} Stats
POST /blood-pressure/import/omron FormData(file) {imported, skipped, failed, errors} Omron CSV-Import (Deutsch)

Contexts: fasting, after_meal, exercise, stress, rest, before_sleep, after_sleep, medication

WHO/ISH-Klassifizierung:

  • Optimal: <120/<80
  • Normal: 120-129/80-84
  • Hoch-Normal: 130-139/85-89
  • Hypertonie Grad 1: 140-159/90-99
  • Hypertonie Grad 2: 160-179/100-109
  • Hypertonie Grad 3: ≥180/≥110

Zusammenfassung

Gesamt: 29 Router-Module, ~200 Endpoints

Authentifizierung: Token-basiert (X-Auth-Token Header)

Fehlerformat: {"detail": "Fehlermeldung"}

Rate Limits: Nur Auth-Endpoints (5/min Login, 3/hour Register)

Feature-Limits: Membership-basiert (v9c), enforcement via HTTP 403

Import-Formate: CSV (Apple Health, Omron, FDDB), JSON (Backup)

Export-Formate: CSV, JSON, ZIP (mit Fotos)

Besonderheiten:

  • Upsert-Logik für viele Endpoints (Date-basiert)
  • Inline-Editing-Support (GET by date, PUT by id)
  • CSV-Import mit Duplikat-Erkennung
  • Lernendes Mapping-System (Activity Types)
  • JSONB für flexible Datenstrukturen (sleep_segments, abilities)
  • Auto-Migration SHA256 → bcrypt
  • E-Mail-Verifizierung + Passwort-Reset