API Documentation

Build integrations with SHELF using our RESTful JSON API. Manage inventory, shopping lists, and storage locations programmatically.

Base URL

https://myshelf.life/api/v1

Authentifizierung

Alle API-Anfragen müssen einen gültigen API-Schlüssel im Authorization-Header enthalten.

Authorization: Bearer YOUR_API_KEY

Berechtigungsstufen

  • Nur lesen: Kann Daten abrufen (GET-Anfragen)
  • Lesen & Schreiben: Kann Daten abrufen und ändern (alle Anfragen)

Fehlerantworten

Bei Fehlern gibt die API einen JSON-Body mit Fehlerdetails zurück.

401 Ungültiger oder fehlender API-Schlüssel
{"error": "Invalid or disabled API key", "code": "unauthorized"}
403 Unzureichende Berechtigungen
{"error": "This API key does not have write permissions", "code": "forbidden"}
404 Ressource nicht gefunden
{"error": "Resource not found", "code": "not_found"}
422 Validierungsfehler
{"error": "Name can't be blank", "code": "unprocessable_entity", "details": {"name": ["can't be blank"]}}

Endpunkte

Lese-Endpunkte

GET /api/v1/locations

Gibt alle aktiven Lagerorte deines Haushalts zurück.

Beispielantwort

{
  "locations": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Kühlschrank",
      "icon": "🧊",
      "location_type": "standard",
      "is_default": true,
      "items_count": 15,
      "created_at": "2024-01-15T10:30:00Z"
    }
  ]
}
GET /api/v1/inventory

Gibt alle Lagerorte mit ihren Artikeln zurück.

Beispielantwort

{
  "locations": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Kühlschrank",
      "icon": "🧊",
      "location_type": "standard",
      "items": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440003",
          "name": "Milch",
          "quantity": 2.0,
          "unit": "Liter",
          "category": "dairy",
          "icon": "🥛",
          "expires_on": "2026-03-01",
          "status": "ok",
          "notes": "Bio",
          "added_by": "Max",
          "created_at": "2026-01-15T10:30:00Z"
        }
      ]
    }
  ]
}
GET /api/v1/shopping_list

Gibt die aktive Einkaufsliste mit allen nicht abgehakten Artikeln zurück.

Beispielantwort

{
  "shopping_list": {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "name": "Einkaufsliste",
    "active_count": 5,
    "items": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440002",
        "name": "Milch",
        "quantity": 2.0,
        "unit": "Liter",
        "category": "dairy",
        "added_by": "Max",
        "created_at": "2024-01-20T14:00:00Z"
      }
    ]
  }
}
GET /api/v1/household_actions

Gibt alle Haushaltsaktionen (aktive und inaktive) mit ihrem aktuellen claimed_today-Status zurück.

Beispielantwort

{
  "household_actions": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Take out trash",
      "emoji": "🗑️",
      "points": 10,
      "active": true,
      "category": "Trash & Recycling",
      "category_emoji": "🗑️",
      "position": 0,
      "claimed_today": true,
      "created_at": "2026-01-15T10:30:00Z"
    }
  ]
}
GET /api/v1/bonus_points

Gibt die Bonuspunkte des API-Nutzers für die aktuelle Woche und insgesamt zurück, sowie das Haushalts-Ranking (ein Eintrag pro Haushaltsmitglied, sortiert nach Wochenpunkten). Rückgängig gemachte Aktivitäten werden nicht gezählt.

Beispielantwort

{
  "week": {
    "start": "2026-05-04",
    "end": "2026-05-10",
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Hendrik",
      "score": 35
    },
    "standings": [
      { "user_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Hendrik", "score": 35, "rank": 1 },
      { "user_id": "660e8400-e29b-41d4-a716-446655440000", "name": "Partner",  "score": 20, "rank": 2 }
    ]
  },
  "all_time": {
    "user": { "score": 1250, "activity_count": 217 }
  }
}
GET /api/v1/leaderboard

Gibt das vollständige Haushalts-Ranking zurück. Anders als bonus_points (nur Bonus-Aktionen) zählt hier der komplette Wochen-Score — hinzugefügte Artikel, Einkaufsliste, Races und Bonus-Aktionen — dieselbe Wertung wie bei den Championships. Mit period=all_time wird die Gesamtwertung (Championship-Siege) zurückgegeben. Gleichstände teilen sich einen Rang.

Query Parameters

Field Type Description
period string Optional. "weekly" (Standard) für die aktuelle Woche oder "all_time" für die Championship-Siege.

Beispielantwort

{
  "period": "weekly",
  "week": { "start": "2026-05-04", "end": "2026-05-10" },
  "user": {
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Hendrik",
    "score": 42,
    "rank": 1,
    "breakdown": { "add_item": 25, "cross_off_shopping": 6, "custom_actions": 11 }
  },
  "standings": [
    { "user_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Hendrik", "score": 42, "rank": 1, "breakdown": { } },
    { "user_id": "660e8400-e29b-41d4-a716-446655440000", "name": "Partner",  "score": 42, "rank": 1, "breakdown": { } }
  ]
}
GET /api/v1/bonus_points/activities

Gibt die letzten Bonus-Aktionen des Haushalts zurück (custom_action-Aktivitäten). Neueste zuerst.

Query Parameters

Field Type Description
limit integer Optional. Maximale Anzahl zurückgegebener Aktivitäten (Standard 50, Maximum 200).
since iso8601 Optional. ISO8601-Zeitstempel; gibt nur Aktivitäten zurück, die ab dieser Zeit erstellt wurden.
user_id uuid Optional. UUID eines Haushaltsmitglieds zum Filtern.
include_undone boolean Optional. "false" angeben, um rückgängig gemachte Aktivitäten auszuschließen (Standard: true).

Beispielantwort

{
  "activities": [
    {
      "id": "770e8400-e29b-41d4-a716-446655440000",
      "household_action": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Coffee in bed",
        "emoji": "☕",
        "points": 10
      },
      "user": { "id": "660e8400-e29b-41d4-a716-446655440000", "name": "Hendrik" },
      "points": 10,
      "created_at": "2026-05-09T08:30:00Z",
      "undone_at": null
    }
  ],
  "count": 1
}

Schreib-Endpunkte

Erforderliche Berechtigung: Lesen & Schreiben

POST /api/v1/inventory/items

Fügt einen neuen Artikel zu deinem Vorrat hinzu. Kategorie und Icon werden automatisch basierend auf dem Artikelnamen zugewiesen.

Anfrage-Body

Field Type Description
name string Erforderlich. Der Artikelname.
location_id uuid Erforderlich. UUID des Lagerorts.
quantity number Optional. Numerische Menge (Standard: 1).
unit string Optional. Maßeinheit.
expires_on date Optional. Ablaufdatum (JJJJ-MM-TT).
notes string Optional. Zusätzliche Notizen.

Beispielanfrage

curl -X POST "https://myshelf.life/api/v1/inventory/items" \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"item": {"name": "Eggs", "location_id": "LOCATION_UUID", "quantity": 6, "expires_on": "2026-03-15"}}'

Beispielantwort (201)

{
  "item": {
    "id": "550e8400-e29b-41d4-a716-446655440010",
    "name": "Eggs",
    "quantity": "6.0",
    "unit": null,
    "category": "dairy",
    "icon": "🥚",
    "expires_on": "2026-03-15",
    "status": "ok",
    "notes": null,
    "added_by": "Max",
    "created_at": "2026-02-15T10:30:00Z"
  }
}
POST /api/v1/shopping_list/items

Fügt einen neuen Artikel zur Einkaufsliste hinzu. Die Kategorie wird automatisch zugewiesen.

Anfrage-Body

Field Type Description
name string Erforderlich. Der Artikelname.
quantity number Optional. Numerische Menge.
unit string Optional. Maßeinheit (z.B. "Liter", "g", "Stück").

Beispielanfrage

curl -X POST "https://myshelf.life/api/v1/shopping_list/items" \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"item": {"name": "Milch", "quantity": 2, "unit": "Liter"}}'

Beispielantwort (201)

{
  "item": {
    "id": "550e8400-e29b-41d4-a716-446655440010",
    "name": "Milch",
    "quantity": "2.0",
    "unit": "Liter",
    "category": "dairy",
    "added_by": "Max",
    "created_at": "2026-02-15T10:30:00Z"
  }
}
PATCH /api/v1/shopping_list/items/:id

Aktualisiert Menge und/oder Einheit eines aktiven Einkaufslisten-Artikels. Abgehakte Artikel können nicht aktualisiert werden (422).

Beispielanfrage

curl -X PATCH "https://myshelf.life/api/v1/shopping_list/items/ITEM_ID" \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"item": {"quantity": 500, "unit": "ml"}}'

Beispielantwort (200)

{
  "item": {
    "id": "550e8400-e29b-41d4-a716-446655440002",
    "name": "Milch",
    "quantity": "500.0",
    "unit": "ml",
    "category": "dairy",
    "added_by": "Max",
    "created_at": "2026-01-20T14:00:00Z"
  }
}
PATCH /api/v1/inventory/items/:id

Aktualisiert einen aktiven Vorratsartikel. Du kannst Menge, Einheit, Lagerort oder Ablaufdatum ändern. Archivierte Artikel geben 404 zurück.

Anfrage-Body

Field Type Description
quantity number Neuer Mengenwert
unit string Neue Maßeinheit
location_id uuid UUID des neuen Lagerorts
expires_on date Neues Ablaufdatum (JJJJ-MM-TT)

Beispielanfrage

curl -X PATCH "https://myshelf.life/api/v1/inventory/items/ITEM_ID" \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"item": {"quantity": 500, "unit": "ml", "location_id": "LOCATION_UUID", "expires_on": "2026-03-15"}}'

Beispielantwort (200)

{
  "item": {
    "id": "550e8400-e29b-41d4-a716-446655440003",
    "name": "Milch",
    "quantity": "500.0",
    "unit": "ml",
    "category": "dairy",
    "icon": "🥛",
    "expires_on": "2026-03-15",
    "status": "ok",
    "notes": "Bio",
    "added_by": "Max",
    "created_at": "2026-01-15T10:30:00Z"
  }
}
POST /api/v1/household_actions/:id/log

Protokolliert eine Haushaltsaktion, um Punkte zu verdienen. Gibt 422 zurück, wenn die Aktion inaktiv ist.

Beispielanfrage

curl -X POST "https://myshelf.life/api/v1/household_actions/ACTION_ID/log" \
     -H "Authorization: Bearer YOUR_API_KEY"

Beispielantwort (201)

{
  "household_action": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Take out trash",
    "emoji": "🗑️",
    "points": 10,
    "active": true,
    "category": "Trash & Recycling",
    "category_emoji": "🗑️",
    "position": 0,
    "claimed_today": true,
    "created_at": "2026-01-15T10:30:00Z"
  },
  "activity": {
    "id": "660e8400-e29b-41d4-a716-446655440000",
    "activity_type": "custom_action",
    "points": 10,
    "created_at": "2026-03-07T14:00:00Z"
  }
}
PATCH /api/v1/household_actions/:id/toggle

Schaltet eine Haushaltsaktion zwischen aktiv und inaktiv um.

Beispielanfrage

curl -X PATCH "https://myshelf.life/api/v1/household_actions/ACTION_ID/toggle" \
     -H "Authorization: Bearer YOUR_API_KEY"

Beispielantwort (200)

{
  "household_action": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Take out trash",
    "emoji": "🗑️",
    "points": 10,
    "active": false,
    "category": "Trash & Recycling",
    "category_emoji": "🗑️",
    "position": 0,
    "claimed_today": false,
    "created_at": "2026-01-15T10:30:00Z"
  }
}
POST /api/v1/activities/:id/undo

Markiert eine zuvor protokollierte Bonus-Aktivität als rückgängig gemacht und entfernt deren Punkte aus den Summen. Jedes Haushaltsmitglied kann jede Haushaltsaktivität rückgängig machen. Gibt 422 zurück, wenn die Aktivität bereits rückgängig gemacht wurde, 404, wenn die Aktivität nicht zu deinem Haushalt gehört oder keine Bonus-Aktion ist.

Beispielanfrage

curl -X POST "https://myshelf.life/api/v1/activities/ACTIVITY_ID/undo" \
     -H "Authorization: Bearer YOUR_API_KEY"

Beispielantwort (200)

{
  "activity": {
    "id": "770e8400-e29b-41d4-a716-446655440000",
    "household_action": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Coffee in bed",
      "emoji": "☕",
      "points": 10
    },
    "user": { "id": "660e8400-e29b-41d4-a716-446655440000", "name": "Hendrik" },
    "points": 10,
    "created_at": "2026-05-09T08:30:00Z",
    "undone_at": "2026-05-09T08:32:14Z"
  }
}
POST /api/v1/activities/:id/redo

Stellt eine zuvor rückgängig gemachte Bonus-Aktivität wieder her und fügt deren Punkte wieder hinzu. Gibt 422 zurück, wenn die Aktivität nicht rückgängig gemacht wurde.

Beispielanfrage

curl -X POST "https://myshelf.life/api/v1/activities/ACTIVITY_ID/redo" \
     -H "Authorization: Bearer YOUR_API_KEY"

Beispielantwort (200)

{
  "activity": {
    "id": "770e8400-e29b-41d4-a716-446655440000",
    "household_action": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Coffee in bed",
      "emoji": "☕",
      "points": 10
    },
    "user": { "id": "660e8400-e29b-41d4-a716-446655440000", "name": "Hendrik" },
    "points": 10,
    "created_at": "2026-05-09T08:30:00Z",
    "undone_at": null
  }
}
DELETE /api/v1/inventory/items/:id

Archiviert (soft-delete) einen Vorratsartikel. Bereits archivierte Artikel geben 404 zurück. Der Artikel kann über die Web-Oberfläche aus dem Archiv wiederhergestellt werden.

Beispielanfrage

curl -X DELETE "https://myshelf.life/api/v1/inventory/items/ITEM_ID" \
     -H "Authorization: Bearer YOUR_API_KEY"

Beispielantwort (200)

{
  "item": {
    "id": "550e8400-e29b-41d4-a716-446655440003",
    "name": "Milch",
    "quantity": "2.0",
    "unit": "Liter",
    "category": "dairy",
    "icon": "🥛",
    "expires_on": "2026-03-01",
    "status": "ok",
    "notes": "Bio",
    "added_by": "Max",
    "created_at": "2026-01-15T10:30:00Z"
  }
}

Meal Plans

Plan meals per day (recipe = title, description, ingredients). Each ingredient may reference an inventory item via item_id, or is matched by name against your active inventory, so responses show what a meal would consume and whether it is in stock. Marking a plan cooked deducts the consumed quantities (and archives an item once it reaches zero). Ideal for agent control.

GET /api/v1/meal_plans

List meal plans in a date range. Each ingredient is resolved against current inventory (in-stock status + matched item). Requires a Read key.

Query Parameters

Field Type Description
from date Start date (YYYY-MM-DD). Defaults to today. Pass an earlier date to browse history.
to date End date (YYYY-MM-DD). Defaults to from + 6 days.

Beispielantwort

{
  "from": "2026-06-04",
  "to": "2026-06-10",
  "meal_plans": [
    {
      "id": "…",
      "date": "2026-06-04",
      "title": "Pasta Pomodoro",
      "description": "Schnelle Tomaten-Pasta",
      "cooked_at": null,
      "in_stock_count": 1,
      "total_ingredients": 2,
      "ingredients": [
        { "name": "Pasta", "quantity": 500, "unit": "g", "item_id": "…", "in_stock": true, "available_quantity": "2.0", "location": "Vorratskammer" },
        { "name": "Basilikum", "quantity": 1, "unit": null, "item_id": null, "in_stock": false }
      ]
    }
  ]
}
POST /api/v1/meal_plans

Create a meal plan for a day. Requires a Read & Write key.

Anfrage-Body

Field Type Description
date string Day the meal is planned for, YYYY-MM-DD (required)
title string Recipe title (required)
description string Short description (optional)
ingredients array [{ name, quantity, unit, item_id }]item_id optional; if omitted it is matched to inventory by name on save (optional)

Beispielanfrage

curl -X POST "https://myshelf.life/api/v1/meal_plans" \
     -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
     -d '{"meal_plan": {"date": "2026-06-04", "title": "Pasta Pomodoro", "ingredients": [{"name": "Pasta", "quantity": 500, "unit": "g"}, {"name": "Tomaten", "quantity": 1, "unit": "Dose"}]}}'

Example Response (201 Created)

{
  "meal_plan": {
    "id": "…",
    "date": "2026-06-04",
    "title": "Pasta Pomodoro",
    "description": null,
    "cooked_at": null,
    "in_stock_count": 1,
    "total_ingredients": 2,
    "ingredients": [
      { "name": "Pasta", "quantity": 500, "unit": "g", "item_id": "…", "in_stock": true, "available_quantity": "2.0", "location": "Vorratskammer" },
      { "name": "Tomaten", "quantity": 1, "unit": "Dose", "item_id": null, "in_stock": false }
    ]
  }
}
PATCH /api/v1/meal_plans/:id

Edit the recipe or move it to another day. Send only the fields you want to change. Requires a Read & Write key.

Anfrage-Body

Field Type Description
date string Move to this day, YYYY-MM-DD (optional)
title string New recipe title (optional)
description string New description (optional)
ingredients array Replace the ingredient list, same shape as POST (optional)

Beispielanfrage

curl -X PATCH "https://myshelf.life/api/v1/meal_plans/MEAL_PLAN_ID" \
     -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
     -d '{"meal_plan": {"date": "2026-06-05"}}'

Example Response (200 OK)

{ "meal_plan": { "id": "…", "date": "2026-06-05", "title": "Pasta Pomodoro", "cooked_at": null, "ingredients": [ … ] } }
POST /api/v1/meal_plans/:id/cook

Mark the plan cooked and deduct the consumed inventory: each matched item is decremented by the recipe amount and archived once it reaches zero. Returns a consumed log. Requires a Read & Write key.

Beispielanfrage

curl -X POST "https://myshelf.life/api/v1/meal_plans/MEAL_PLAN_ID/cook" \
     -H "Authorization: Bearer YOUR_API_KEY"

Example Response (200 OK)

{
  "meal_plan": { "id": "…", "cooked_at": "2026-06-04T18:30:00Z", "…": "…" },
  "consumed": [
    { "item_id": "…", "name": "Pasta", "action": "decremented", "used": 500, "remaining": "1500.0" },
    { "item_id": "…", "name": "Tomaten", "action": "archived", "used": 1, "remaining": 0 }
  ]
}
DELETE /api/v1/meal_plans/:id

Soft-delete (archive) a meal plan. Requires a Read & Write key.

Beispielanfrage

curl -X DELETE "https://myshelf.life/api/v1/meal_plans/MEAL_PLAN_ID" \
     -H "Authorization: Bearer YOUR_API_KEY"

Example Response (200 OK)

{ "meal_plan": { "id": "…", "date": "2026-06-04", "title": "Pasta Pomodoro", "…": "…" } }

Beispiel mit curl

Hier ist ein Beispiel, wie du die API mit curl aufrufen kannst:

# Read shopping list
curl -H "Authorization: Bearer YOUR_API_KEY" \
     https://myshelf.life/api/v1/shopping_list
# Add item to shopping list
curl -X POST "https://myshelf.life/api/v1/shopping_list/items" \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"item": {"name": "Milch", "quantity": 2, "unit": "Liter"}}'