book2eat Supplier API

Schnittstelle für die Übergabe von Speiseplänen an book2eat und den Abruf von Bestellzahlen.
Version 1.

Basis-URL: https://supplier-api.book2eat.eu/v1 ⭳ Postman-Collection (Live) herunterladen

Authentifizierung

Alle Endpoints außer /v1/health erfordern HTTP Basic Auth über HTTPS. Die Zugangsdaten werden von tech2solutions vergeben — Anfragen dazu bitte an info@book2eat.eu. Jeder API-User ist für einen oder mehrere Standorte freigeschaltet — Requests für nicht freigeschaltete Standorte werden mit 403 abgelehnt.

curl -u benutzername:passwort https://supplier-api.book2eat.eu/v1/locations

Damit lässt sich der eigene Zugang testen, ohne bereits eine echte Speiseplan-ID zu kennen — die Antwort zeigt die freigeschalteten Standorte (siehe unten).

Datenformat

Alle Request- und Response-Bodies sind JSON (application/json; charset=utf-8). Datumsangaben: YYYY-MM-DD, Zeitstempel: YYYY-MM-DD HH:MM:SS (lokale Zeit, Europa/Wien).

Deadlines: orderDeadline kann pro menuItem mitgegeben werden und übersteuert dann den Wert auf Speiseplan-Ebene. cancelDeadline ist optional — fehlt sie, wird automatisch orderDeadline + 7 Tage gesetzt. minOrderQuantity wird entgegengenommen, aber derzeit nicht ausgewertet. Sobald bei einem menuItem sowohl orderDeadline als auch cancelDeadline verstrichen sind, gilt es als final (final: true) und kann über die API nicht mehr geändert oder gelöscht werden (409 ITEM_LOCKED) — die Bestellzahl steht dann endgültig fest.

Preis: price ist auf jedem menuItem optional. Fehlt es, wird der Preis wie gehabt aus dem in book2eat gepflegten Menütyp übernommen; wird es mitgeschickt, überschreibt es diesen Preis für den jeweiligen Eintrag. Umsatzsteuer (vat) kommt immer aus book2eat und kann nicht per API gesetzt werden.

Freigeschaltete Standorte abrufen

GET/v1/locations

Listet die Standorte, für die der authentifizierte API-User freigeschaltet ist — damit lässt sich vorab prüfen, ob die eigene locationClientUid korrekt mit book2eat verknüpft ist, bevor ein Speiseplan gesendet wird. linked: false bedeutet, dass der Standort zwar freigeschaltet, aber noch keiner locationClientUid zugeordnet ist — in dem Fall bitte tech2solutions kontaktieren. active spiegelt das bestehende Aktiv-Flag der Einrichtung in book2eat: ist eine Einrichtung hier false, schlägt POST/PUT mit 422 UNKNOWN_LOCATION fehl, auch wenn linked: true ist — book2eat findet die deaktivierte Einrichtung über locationClientUid dann nicht mehr.

Response · 200 OK
[
  {
    "canteen_ID": 271,
    "canteenName": "Kindergarten Obersulz",
    "locationClientUid": "130669",
    "locationId": "642",
    "active": true,
    "linked": true
  }
]

Menülinien-Mapping abrufen

GET/v1/menus/lines?locationClientUid={locationClientUid}

Listet die für diesen Standort in book2eat gepflegten Menülinien-Mappings (menuLines[].id → book2eat-Menütyp inkl. aktuellem Preis/USt.) — damit lässt sich vorab prüfen, ob eine ADAM-Menülinie korrekt verknüpft ist, bevor ein Speiseplan gesendet wird. Fehlt eine extLineId hier, liefert POST/PUT für genau diese Linie später 422 UNKNOWN_MENU_LINE — in dem Fall bitte tech2solutions kontaktieren, damit die Zuordnung ergänzt wird.

Response · 200 OK
[
  {
    "extLineId": "4056",
    "lineName": "Tagessuppe",
    "typ_ID": 1,
    "name": "Suppe",
    "standard_price": "3.50",
    "vat": "10.00"
  },
  {
    "extLineId": "4057",
    "lineName": "Hauptspeise",
    "typ_ID": 2,
    "name": "Hauptgericht",
    "standard_price": "8.90",
    "vat": "10.00"
  }
]

Speiseplan anlegen

POST/v1/menus

Legt einen Wochenspeiseplan an. Die Speiseplan-ID (id) sowie alle menuItem-IDs sind die IDs des sendenden Systems und bleiben in allen weiteren Requests die Referenz. Da es sich um einen neuen Speiseplan handelt, existieren noch keine Bestellungen dazu — es wird keine Benachrichtigung verschickt.

Request-Body (gekürzt)
{
  "id": "717304",
  "locationId": "642",
  "locationName": "Kindergarten Obersulz",
  "locationClientUid": "130669",
  "week": "17",
  "year": "2026",
  "startDate": "2026-04-20",
  "endDate": "2026-04-26",
  "orderDeadline": "2026-04-06 23:59:59",
  "menuLines": [
    {
      "id": "4056",
      "name": "Tagessuppe",
      "minOrderQuantity": "0",
      "menuItems": [
        {
          "id": "15323144",
          "name": "Klare Hühnersuppe mit Reibteig",
          "date": "2026-04-20",
          "isExtraMenu": false,
          "allergens": ["A", "C"],
          "additives": [],
          "attributes": ["enthält Gluten"],
          "nutritionValues": { "kJ": 203, "kcal": 49 }
        }
      ]
    }
  ]
}
Response · 201 Created
{ "id": "717304", "status": "created", "itemsImported": 10 }

Existiert die Speiseplan-ID bereits, antwortet die API mit 409 Conflict — Änderungen laufen über PUT.

Speiseplan abrufen

GET/v1/menus/{id}

Liefert den aktuell in book2eat hinterlegten Stand des Speiseplans {id} — Name, Datum, Allergene und Deadlines je menuItem, so wie sie aktuell gespeichert sind (nicht zwingend identisch mit dem ursprünglich gesendeten Payload, falls es zwischenzeitlich Änderungen gab). additives, attributes und nutritionValues werden von book2eat nicht gespeichert und sind daher hier nicht enthalten.

Response · 200 OK
{
  "id": "717304",
  "locationClientUid": "130669",
  "week": 17,
  "year": 2026,
  "startDate": "2026-04-20",
  "endDate": "2026-04-26",
  "menuLines": [
    {
      "id": "4056",
      "name": "Tagessuppe",
      "menuItems": [
        {
          "id": 15323144,
          "name": "Klare Hühnersuppe mit Reibteig",
          "date": "2026-04-20",
          "allergens": ["A", "C"],
          "orderDeadline": "2026-04-06 23:59:59",
          "cancelDeadline": "2026-04-13 23:59:59",
          "orderDeadlinePassed": true,
          "cancelDeadlinePassed": true,
          "final": true
        }
      ]
    }
  ]
}

Bestellzahlen abrufen

GET/v1/menus/{id}/quantities

Liefert die Bestellzahlen je menuItem für den Speiseplan {id}. Einträge ohne Bestellungen werden mit quantity: 0 ausgegeben. final zeigt an, ob die Zahl schon endgültig ist — also sowohl orderDeadline als auch cancelDeadline bereits verstrichen sind und sich die Bestellzahl nicht mehr ändern kann.

Response · 200 OK
[
  {
    "id": 15323144,
    "date": "2026-04-20",
    "quantity": 15,
    "orderDeadline": "2026-04-06 23:59:59",
    "cancelDeadline": "2026-04-13 23:59:59",
    "orderDeadlinePassed": true,
    "cancelDeadlinePassed": true,
    "final": true
  }
]

Bestellzahlen über Woche/Zeitraum abrufen

GET/v1/menus/quantities

Liefert Bestellzahlen kantinenweit über mehrere Speisepläne hinweg — wahlweise für eine ganze Woche oder einen freien Datumsbereich, ohne dass die einzelne Speiseplan-ID bekannt sein muss. Erfordert locationClientUid sowie entweder week+year oder from+to (YYYY-MM-DD) als Query-Parameter. planId im Ergebnis verweist auf die zugehörige Speiseplan-ID.

Zeitraum-Grenzen: Der angefragte Zeitraum darf höchstens 400 Tage in der Vergangenheit beginnen und darf insgesamt höchstens 180 Tage umfassen (bei week+year anhand der tatsächlichen Kalenderwoche geprüft). Außerhalb dieser Grenzen antwortet die API mit 400 VALIDATION_ERROR.
Beispiel
curl -u benutzername:passwort \
  "https://supplier-api.book2eat.eu/v1/menus/quantities?locationClientUid=130669&week=17&year=2026"

curl -u benutzername:passwort \
  "https://supplier-api.book2eat.eu/v1/menus/quantities?locationClientUid=130669&from=2026-04-20&to=2026-04-26"
Response · 200 OK
[
  {
    "id": 15323144,
    "planId": "717304",
    "date": "2026-04-20",
    "quantity": 15,
    "orderDeadline": "2026-04-06 23:59:59",
    "cancelDeadline": "2026-04-13 23:59:59",
    "orderDeadlinePassed": true,
    "cancelDeadlinePassed": true,
    "final": true
  }
]

Speiseplan komplett ersetzen

PUT/v1/menus/{id}

Ersetzt den Speiseplan durch den mitgeschickten Body (gleiche Struktur wie POST). Neue Einträge werden angelegt, bestehende (gleiche menuItem-ID) aktualisiert. Einträge, die im Body fehlen, werden entfernt — vorhandene Bestellungen dazu werden storniert. menuItems, die bereits final sind (siehe oben), bleiben unangetastet — weder Änderung noch Entfernung — und zählen in der Antwort als protected. Der restliche Speiseplan wird ganz normal verarbeitet.

Benachrichtigung: Für sowohl aktualisierte als auch entfernte menuItems werden Besteller mit einer aktiven Bestellung (nicht bereits stornierte) automatisch von book2eat per E-Mail benachrichtigt. Für neu angelegte Einträge sowie für protected Einträge gibt es keine Benachrichtigung.
Response · 200 OK
{ "id": "717304", "status": "replaced", "created": 1, "updated": 8, "removed": 1, "protected": 0, "cancelledOrders": 12 }

Einzelnen Eintrag ändern

PUT/v1/menus/{id}/items/{itemId}

Detailänderung eines einzelnen Speiseplaneintrags — z. B. Komponententausch oder geändertes Bestellende. Erlaubte Felder: name, date, allergens, orderDeadline, cancelDeadline, price (optional). Nur mitgeschickte Felder werden geändert. Besteller mit einer aktiven Bestellung für diesen Eintrag werden automatisch von book2eat per E-Mail benachrichtigt. Ist das Item bereits final, antwortet die API mit 409 ITEM_LOCKED.

Request-Body (Beispiel: Komponententausch + neues Bestellende)
{
  "name": "Kürbiscremesuppe mit Kernöl",
  "allergens": ["G"],
  "orderDeadline": "2026-04-08 23:59:59"
}
Response · 200 OK
{ "id": "15323144", "status": "updated" }

Löschen

DELETE/v1/menus/{id}

Löscht den kompletten Speiseplan. Alle offenen Bestellungen werden storniert. Bereits finale menuItems (siehe oben) werden dabei übersprungen statt gelöscht/storniert und zählen in der Antwort als protected.

Benachrichtigung: Besteller mit einer aktiven Bestellung für ein betroffenes menuItem werden automatisch von book2eat per E-Mail benachrichtigt. Für protected Einträge gibt es keine Benachrichtigung.
Response · 200 OK
{ "id": "717304", "deleted": true, "items": 9, "protected": 1, "cancelledOrders": 33 }
DELETE/v1/menus/{id}/items/{itemId}

Löscht einen einzelnen Speiseplaneintrag inklusive Storno der zugehörigen Bestellungen. Besteller mit einer aktiven Bestellung für diesen Eintrag werden automatisch von book2eat per E-Mail benachrichtigt. Ist das Item bereits final, antwortet die API mit 409 ITEM_LOCKED.

Response · 200 OK
{ "id": "15323144", "deleted": true, "cancelledOrders": 4 }

Fehlercodes

StatusCodeBedeutung
400VALIDATION_ERROR / INVALID_JSONBody entspricht nicht dem Schema bzw. ist kein gültiges JSON. details enthält die Feldfehler.
401UNAUTHORIZEDFehlende oder falsche Zugangsdaten.
403CANTEEN_NOT_ALLOWEDDer API-User ist für diesen Standort nicht freigeschaltet.
404MENU_NOT_FOUND / ITEM_NOT_FOUNDSpeiseplan bzw. Eintrag existiert nicht.
409MENU_EXISTSSpeiseplan-ID bereits vorhanden — PUT verwenden.
409ITEM_ID_EXISTSDie menuItem-ID wird bereits in einem anderen Speiseplan verwendet — menuItem-IDs müssen global eindeutig sein, nicht nur innerhalb eines Speiseplans.
409ITEM_LOCKEDorder- und cancelDeadline des menuItems sind bereits verstrichen — keine Änderung/Löschung mehr möglich.
422UNKNOWN_LOCATION, DATE_OUT_OF_RANGE, DUPLICATE_ITEM_IDFachlich ungültig — Meldung beschreibt das Problem.
429Rate-Limit erreicht. Kurz warten und erneut senden.
Fehlerformat
{
  "error": {
    "code": "UNKNOWN_LOCATION",
    "message": "Der Standort mit locationClientUid 130699 ist in book2eat nicht angelegt.",
    "details": null
  }
}

Health-Check

GET/v1/health

Ohne Authentifizierung erreichbar, für Monitoring.

{ "status": "ok", "db": "ok", "time": "2026-07-14T09:00:00.000Z" }