Vai al contenuto
v26.3

API-Referenz: Webshop-Anbindung

Diese Seite ist die Nachschlage-Referenz zum Leitfaden Eigenen Webshop anbinden: alle Aufrufe, die ein eigener Webshop (oder eine Webagentur) an der Kasse eines Betriebs nutzen kann, mit Anmeldung, Anfrage- und Antwortfeldern, Fehlercodes und Beispielen. Sie behandelt ausschließlich die aktuelle Schnittstelle unter /api/v1/…; ältere Schnittstellen sind für neue Anbindungen nicht vorgesehen und hier nicht beschrieben.

OpenAPI-Datei herunterladen (openapi-webshop.json)

Die Datei enthält dieselben Aufrufe und Datenobjekte als OpenAPI-3-Beschreibung, zum Beispiel für Postman, Insomnia oder einen Client-Generator. Als Server ist https://{betrieb}/api/v1 eingetragen; betrieb ist die Adresse der Kasse des Betriebs.

Grundlagen

Adresse. Alle Aufrufe gehen an die Kasse des Betriebs: https://<betrieb>.<ihre-domain>/api/v1/…. In den Beispielen steht API für diese Adresse einschließlich /api/v1.

Antwortformat. Jede Antwort ist in eine Hülle verpackt: { "success": true, "data": … }. Bei einem Fehler ist success gleich false, data ist null, und errorCode (stabil, für Programme) sowie errorMessage (für Menschen) benennen den Grund; bei fehlerhaften Eingaben listet validationErrors Feld und Meldung. Prüfen Sie immer zuerst success. Die Tabellen unten beschreiben jeweils den Inhalt von data.

Schreibweise. Feldnamen sind camelCase. Beträge sind Dezimalzahlen in der Währung des Betriebs (9.5). Zeitpunkte sind ISO-8601 (2026-11-10T17:44:05Z), in der Regel UTC. Kennungen (art_…, olord_…) sind undurchsichtig: nicht zerlegen, nur weitergeben. Ein ? hinter dem Typ heißt: der Wert kann null sein. Unbekannte zusätzliche Felder in Antworten sind zu ignorieren.

Wiederholsicherheit. Bei POST, PUT, PATCH und DELETE kann die Kopfzeile Idempotency-Key (8 bis 200 Zeichen) mitgesendet werden: derselbe Aufruf mit demselben Schlüssel und Inhalt innerhalb einer Stunde liefert das erste Ergebnis erneut, statt ihn zweimal auszuführen; derselbe Schlüssel mit anderem Inhalt ergibt 422 IDEMPOTENCY_KEY_REUSED. Für Bestellungen gilt das dauerhaft, siehe Bestellung anlegen.

Teiländerungen an Artikeln und Warengruppen laufen als JSON Patch (RFC 6902, Content-Type: application/json-patch+json): eine Liste von Operationen add, replace, remove und test mit Pfad (/price) und Wert. Eine test-Operation vor der Änderung schützt vor dem Überschreiben fremder Änderungen: hat sich der Wert inzwischen geändert, antwortet die Kasse 409 PATCH_TEST_FAILED und schreibt nichts.

Zugangsdaten sind Geheimnisse. Partner-Schlüssel, Bestell-Geheimnisse und Webhook-Geheimnisse gehören nur in Ihren Server, nie in Browser-Code, Repositories oder Protokolle.

Anmeldearten

Kürzel Bedeutung Kopfzeile
keine Öffentlich erreichbar, ohne Kopfzeile. Gedacht für den Browser des Gastes und für Shop-Server. –
Bestell-Geheimnis Das Geheimnis, das die Kasse beim Anlegen einer Bestellung genau einmal herausgibt. Es gehört zu genau einer Bestellung. X-Order-Secret
Partner-Schlüssel oder Bearer Server-zu-Server-Zugang eines Fremdsystems. Der Schlüssel hat feste Bereiche (orders:read, orders:manage, tables:read, events:read) und gehört zu einem technischen Benutzer, dessen Rechte zusätzlich gelten. Ausgestellt unter Einstellungen → System → Partner-Schlüssel oder per Partner-Schlüssel anlegen. X-Dikas-Api-Key: dk1_…
Bearer Ein Kassenbenutzer meldet sich mit POST /api/v1/auth/login an ({"username":…,"password":…}, Antwort data.accessToken). Für Anbindungen empfiehlt sich ein eigener Benutzer mit nur den nötigen Rechten. Authorization: Bearer …
Administrator (Bearer) Wie Bearer, aber nur mit Administratorrolle. Gilt für die Verwaltung von Webhook-Zielen und Partner-Schlüsseln. Authorization: Bearer …

Fehler, die jeder Endpunkt kennen kann

HTTP errorCode Bedeutung
400 VALIDATION_ERROR Eingabe fehlerhaft; validationErrors nennt je Feld die Meldung.
401 (kein Inhalt) Anmeldung fehlt, ist abgelaufen oder ungültig; ebenso ein ungültiger, abgelaufener oder widerrufener Partner-Schlüssel, oder ein Partner-Schlüssel an einem Aufruf, der nur Benutzer-Anmeldung annimmt.
403 (kein Inhalt) Ein gültiger Partner-Schlüssel hat den Bereich des Aufrufs nicht, oder dem Benutzer fehlt die nötige Rolle bzw. das nötige Recht.
403 MODULE_NOT_LICENSED Das Modul (Online-Bestellung) ist in der Lizenz des Betriebs nicht enthalten.
404 ENTITY_NOT_FOUND Die Kennung ist unbekannt. Bei Bestell-Aufrufen mit Bestell-Geheimnis auch: Geheimnis fehlt oder passt nicht.
404 SWITCH_DISABLED Online-Bestellung ist im Betrieb ausgeschaltet (Admin → Einstellungen → Betrieb).
429 (kein Inhalt) Zu viele fehlgeschlagene Schlüsselprüfungen von derselben Adresse (30 je Minute); später erneut versuchen.

Übersicht

Aufruf Zweck Anmeldung
GET /online-menu/menu Speisekarte lesen keine
GET /online-menu/status Öffnungsstatus lesen keine
GET /online-menu/wait-estimate Wartezeit lesen keine
GET /online-menu/shop-info Shop-Angaben lesen keine
GET /article-descriptions/{articleId}/images/{attachmentName} Artikelbild laden keine
POST /online-checkout/create Bestellung anlegen keine
GET /online-checkout/{id} Bestellung lesen Bestell-Geheimnis
POST /online-checkout/{id}/init-payment Online-Zahlung starten Bestell-Geheimnis
POST /online-checkout/{id}/verify-payment Zahlung prüfen Bestell-Geheimnis
POST /online-checkout/{id}/cancel Bestellung stornieren Bestell-Geheimnis
GET /online-checkout/pending Offene Bestellungen lesen Partner-Schlüssel (orders:read) oder Bearer
GET /online-checkout/today Heutige Bestellungen lesen Partner-Schlüssel (orders:read) oder Bearer
POST /online-checkout/{id}/confirm Bestellung annehmen Partner-Schlüssel (orders:manage) oder Bearer
POST /online-checkout/{id}/reject Bestellung ablehnen Partner-Schlüssel (orders:manage) oder Bearer
PUT /online-checkout/{id}/status Bestellstatus setzen Partner-Schlüssel (orders:manage) oder Bearer
GET /articles/groups Warengruppen lesen Bearer
POST /articles/groups Warengruppe anlegen Bearer
PATCH /articles/groups/{documentId} Warengruppe ändern Bearer
GET /articles Artikel lesen Bearer
GET /articles/{id} Einen Artikel lesen Bearer
POST /articles Artikel anlegen Bearer
PATCH /articles/{id} Artikel ändern Bearer
GET /events Ereignisse lesen Partner-Schlüssel (events:read) oder Bearer
GET /webhook-targets Webhook-Ziele lesen Administrator (Bearer)
POST /webhook-targets Webhook-Ziel anlegen Administrator (Bearer)
PATCH /webhook-targets/{id} Webhook-Ziel ändern Administrator (Bearer)
POST /webhook-targets/{id}/rotate-secret Webhook-Geheimnis erneuern Administrator (Bearer)
DELETE /webhook-targets/{id} Webhook-Ziel löschen Administrator (Bearer)
GET /webhook-targets/deliveries Zustellungen lesen Administrator (Bearer)
POST /webhook-targets/deliveries/retry Tote Zustellungen wiederholen Administrator (Bearer)
GET /partner-api-keys/scopes Bereiche lesen Administrator (Bearer)
GET /partner-api-keys Partner-Schlüssel lesen Administrator (Bearer)
POST /partner-api-keys Partner-Schlüssel anlegen Administrator (Bearer)
POST /partner-api-keys/{id}/rotate Partner-Schlüssel erneuern Administrator (Bearer)
DELETE /partner-api-keys/{id} Partner-Schlüssel widerrufen Administrator (Bearer)

Speisekarte und Öffnungsstatus

Diese vier Aufrufe sind öffentlich und können direkt aus dem Browser des Gastes oder vom Shop-Server gerufen werden. Sie setzen voraus, dass Online-Bestellung im Betrieb eingeschaltet ist (sonst 404 SWITCH_DISABLED).

Speisekarte lesen

GET /api/v1/online-menu/menu

Liefert die veröffentlichte Karte, gegliedert nach Warengruppen, mit Preisen, Optionen, Allergenen und Bildern. Ausgeblendete Gruppen und Artikel fehlen. Ein Artikel mit soldOut: true steht auf der Karte, wird beim Bestellen aber abgewiesen. Die Kasse rechnet beim Bestellen mit ihren Preisen: die Karte daher nicht lange zwischenspeichern.

Anmeldung: Keine Anmeldung, der Aufruf ist öffentlich.

Antwort 200

data: OnlineMenuResponse

Feld Typ Bedeutung
shopName Text? Name des Shops.
shopDescription Text? Kurzbeschreibung des Shops.
shopSlogan Text? Slogan des Shops.
groups Liste von OnlineMenuGroup? Die Warengruppen mit ihren Artikeln. Nur Gruppen und Artikel, die online sichtbar sind.
allergenDefinitions Liste von OnlineMenuAllergen? Klartext zu den Allergen-Kürzeln der Artikel (allergens).

Enthaltene Datenobjekte: OnlineMenuAllergen, OnlineMenuGroup.

Fehler

HTTP errorCode Bedeutung
404 SWITCH_DISABLED Online-Bestellung ist im Betrieb ausgeschaltet.
403 MODULE_NOT_LICENSED Das Modul Online-Bestellung fehlt in der Lizenz.

Beispiel

curl -sS "$API/online-menu/menu" | jq -c '.data | {shopName, groups: [.groups[] | {id, name, articles: [.articles[] | {id, name, price, soldOut}]}]}'

Gekürzt mit jq; die Antwort enthält je Artikel alle Felder der Tabelle.

{"shopName": "Pizzeria Beispiel", "groups": [{"id": "artgrp_2d6002dced5a", "name": "Pizza", "articles": [{"id": "art_917c19d4a18a", "name": "Pizza Margherita", "price": 9.5, "soldOut": false}, {"id": "art_ae2ef2e45883", "name": "Pizza Salami", "price": 11, "soldOut": false}]}]}

Öffnungsstatus lesen

GET /api/v1/online-menu/status

Meldet, ob der Betrieb gerade innerhalb der Öffnungszeiten ist, und wann er schließt bzw. das nächste Mal öffnet (Öffnungszeiten und besondere Schließtage des Betriebs).

Anmeldung: Keine Anmeldung, der Aufruf ist öffentlich.

Parameter

Name Ort Typ Pflicht Bedeutung
channel Abfrage Text nein Bestellweg: OnlineOrder (Vorgabe), QrTable oder Reservation. Jeder Weg fragt seinen eigenen Betriebsschalter ab.

Antwort 200

data: ShopStatusResponse

Feld Typ Bedeutung
isOpen Ja/Nein true: innerhalb der Öffnungszeiten.
currentCloseTime Text? Heutige Schließzeit (22:00), solange geöffnet.
nextOpenDate Text? Datum der nächsten Öffnung (2026-11-12), wenn geschlossen.
nextOpenClockTime Text? Uhrzeit der nächsten Öffnung (11:00), wenn geschlossen.
nextOpenDaysAhead Ganzzahl? Tage bis zur nächsten Öffnung; 0 = heute noch.
nextOpenTime Text? Veraltet: nächste Öffnung als fertiger deutscher Text. Neue Anbindungen verwenden nextOpenDate und nextOpenClockTime.

Fehler

HTTP errorCode Bedeutung
404 SWITCH_DISABLED Der angefragte Bestellweg ist im Betrieb ausgeschaltet.

Beispiel

curl -sS "$API/online-menu/status" | jq -c .data
{"isOpen": true, "nextOpenTime": null, "nextOpenDate": null, "nextOpenClockTime": null, "nextOpenDaysAhead": null, "currentCloseTime": "22:00"}

Wartezeit lesen

GET /api/v1/online-menu/wait-estimate

Schätzt aus der Küchenlast, wann eine Abholung fertig bzw. eine Lieferung da wäre, und ob der Betrieb gerade annimmt. Die Kasse hält die Auskunft je Betrieb 30 Sekunden vor; häufiger abzufragen bringt nichts.

Anmeldung: Keine Anmeldung, der Aufruf ist öffentlich.

Antwort 200

data: WaitEstimateResponse

Feld Typ Bedeutung
pickupMinutes Ganzzahl Minuten bis zur Abholung.
deliveryMinutes Ganzzahl Minuten bis zur Lieferung.
load Text? Auslastung der Küche: low, normal oder high.
readyAt Zeitpunkt Voraussichtlich fertig (Abholung).
deliveryAt Zeitpunkt Voraussichtlich geliefert.
acceptingOrders Ja/Nein false: der Betrieb nimmt wegen Küchenlast gerade nicht an.

Fehler

HTTP errorCode Bedeutung
404 SWITCH_DISABLED Online-Bestellung ist im Betrieb ausgeschaltet.

Beispiel

curl -sS "$API/online-menu/wait-estimate" | jq -c .data
{"pickupMinutes": 15, "deliveryMinutes": 60, "tableMinutes": 15, "load": "low", "readyAt": "2026-11-10T18:58:36.3342666+01:00", "deliveryAt": "2026-11-10T19:43:36.3342666+01:00", "acceptingOrders": true, "openTickets": 0}

Shop-Angaben lesen

GET /api/v1/online-menu/shop-info

Öffentliche Angaben zum Shop: Name, Anschrift und Kontakt, Öffnungszeiten, angebotene Bestellwege und Zahlarten, Mindestbestellwert. Daraus lässt sich im Shop zum Beispiel ablesen, ob PayPal angeboten wird oder Sofortzahlung verlangt ist.

Anmeldung: Keine Anmeldung, der Aufruf ist öffentlich.

Antwort 200

data: OnlineShopConfigResponse

Feld Typ Bedeutung
shopName Text? Name des Shops.
shopDescription Text? Beschreibung.
shopSlogan Text? Slogan.
companyName Text? Firmenname.
street Text? Straße und Hausnummer.
zipCode Text? Postleitzahl.
city Text? Ort.
country Text? Land.
phone Text? Telefon.
email Text? E-Mail-Adresse.
openingHours Liste von OpeningHoursEntry? Öffnungszeiten je Wochentag.
pickupEnabled Ja/Nein Abholung wird angeboten.
deliveryEnabled Ja/Nein Lieferung wird angeboten.
minOrderAmount Dezimalzahl Mindestbestellwert.
orderLeadTimeMinutes Ganzzahl Vorlaufzeit für Bestellungen in Minuten.
maxScheduleDays Ganzzahl Wie viele Tage im Voraus bestellt werden darf.
stripeEnabled Ja/Nein Kartenzahlung über Stripe ist eingeschaltet.
payPalEnabled Ja/Nein PayPal ist eingeschaltet.
cashOnPickupEnabled Ja/Nein Barzahlung bei Abholung ist erlaubt.
cashOnDeliveryEnabled Ja/Nein Barzahlung bei Lieferung ist erlaubt.
prepaymentRequiredPickup Ja/Nein Abholung nur mit Online-Zahlung.
prepaymentRequiredDelivery Ja/Nein Lieferung nur mit Online-Zahlung.

Enthaltene Datenobjekte: OpeningHoursEntry.

Beispiel

curl -sS "$API/online-menu/shop-info" | jq -c '.data | {shopName, pickupEnabled, deliveryEnabled, minOrderAmount, stripeEnabled, payPalEnabled, cashOnPickupEnabled}'
{"shopName": "Pizzeria Beispiel", "pickupEnabled": true, "deliveryEnabled": true, "minOrderAmount": 0, "stripeEnabled": false, "payPalEnabled": false, "cashOnPickupEnabled": true}

Artikelbild laden

GET /api/v1/article-descriptions/{articleId}/images/{attachmentName}

Liefert ein Bild eines Artikels als Datei. Den Dateinamen nennt die Karte in images[].attachmentName (bzw. thumbnailName für die kleine Ansicht).

Anmeldung: Keine Anmeldung, der Aufruf ist öffentlich.

Parameter

Name Ort Typ Pflicht Bedeutung
articleId Pfad Text ja Kennung des Artikels (art_…).
attachmentName Pfad Text ja Dateiname aus images[].attachmentName.

Antwort 200

Das Bild als Datei, in der Regel image/webp. Ein unbekannter Name ergibt 404.

Beispiel

curl -sS "$API/article-descriptions/$ARTIKEL/images/$BILDNAME" -o artikel.webp

Online-Bestellung

Der Weg einer Bestellung: anlegen, Stand abfragen, gegebenenfalls online bezahlen; auf der Seite des Betriebs annehmen oder ablehnen. Der Ablauf im Zusammenhang steht im Leitfaden Eigenen Webshop anbinden.

Bestellung anlegen

POST /api/v1/online-checkout/create

Legt eine Bestellung an und übergibt sie an die Kasse: Die Kasse prüft Öffnungszeiten, Verfügbarkeit, Optionen und Liefergebiet, rechnet mit ihren eigenen Preisen und legt einen Auftrag im Lieferservice an. Die Antwort enthält einmalig das Bestell-Geheimnis (guestSecret).

Anmeldung: Keine Anmeldung, der Aufruf ist öffentlich.

Parameter

Name Ort Typ Pflicht Bedeutung
Idempotency-Key Kopfzeile Text nein Neuer Wert je Bestellung (8 bis 200 Zeichen), zum Beispiel eine UUID. Wiederholt der Shop den Aufruf nach einem Netzfehler mit demselben Schlüssel und Inhalt, kommt dieselbe Bestellung zurück. Anderer Inhalt: 422 IDEMPOTENCY_KEY_REUSED. Dringend empfohlen.

Anfrage (application/json)

Die Preise bestimmt die Kasse; ein vom Shop mitgesendeter Preis zählt nicht. Bei Optionen genügt der Name aus der Karte.

Feld Typ Pflicht Bedeutung
orderType Ganzzahl ja 0 Abholung, 1 Lieferung. (2 ist die QR-Tischbestellung der Kasse und für Shops nicht vorgesehen.)
customerName Text ja Name des Gastes, höchstens 200 Zeichen.
customerEmail Text ja E-Mail-Adresse des Gastes, gültig, höchstens 200 Zeichen. Die Bestätigung geht dorthin.
customerPhone Text nein Telefon, höchstens 30 Zeichen. Für Lieferungen sinnvoll.
items Liste von OnlineOrderItemRequest ja Die Positionen, mindestens eine.
paymentMethod Text nein cash, stripe oder paypal; immer angeben. Verlangt der Betrieb Sofortzahlung (prepaymentRequired… in Shop-Angaben), ist nur stripe oder paypal zulässig, sonst PREPAYMENT_REQUIRED.
street Text bedingt Straße, bei Lieferung Pflicht.
houseNumber Text nein Hausnummer (höchstens 20 Zeichen).
zipCode Text bedingt Postleitzahl, bei Lieferung Pflicht.
city Text bedingt Ort, bei Lieferung Pflicht.
deliveryNotes Text nein Hinweis für den Fahrer, höchstens 2000 Zeichen.
floor Text nein Stockwerk.
doorBell Text nein Klingelschild.
requestedTime Zeitpunkt nein Wunschzeitpunkt (ISO-8601). Ohne Angabe so bald wie möglich.
kitchenNotes Text nein Hinweis für die Küche.
tipAmount Dezimalzahl nein Trinkgeld, 0 bis 10 000; nur mit Online-Zahlung, sonst TIP_ONLINE_ONLY.
externalReference Text nein Eigene Bestellnummer des Shops, höchstens 100 Zeichen, ohne Steuerzeichen. Nur mit Benutzer-Anmeldung (Bearer) wirksam; ohne Anmeldung und mit Partner-Schlüssel wird sie ignoriert. Dieselbe Nummer mit demselben Inhalt liefert die vorhandene Bestellung zurück, mit anderem Inhalt 409 EXTERNAL_REFERENCE_CONFLICT.

Enthaltene Datenobjekte: OnlineOrderItemRequest.

Antwort 200

data: OnlineOrderResponse

Fehler

HTTP errorCode Bedeutung
400 VALIDATION_ERROR Pflichtfeld fehlt oder ist ungültig (keine Position, Menge außerhalb 1 bis 999, Lieferadresse fehlt, ungültige E-Mail-Adresse).
400 SHOP_CLOSED Außerhalb der Öffnungszeiten.
400 SHOP_OVERLOADED Der Betrieb nimmt wegen Küchenlast gerade nicht an.
400 ARTICLE_SOLD_OUT Ein Artikel ist heute nicht verfügbar (soldOut).
400 ARTICLE_POS_ONLY Ein Artikel ist nur an der Kasse verkäuflich (zum Beispiel Gutscheine und Guthaben-Aufladungen).
404 ENTITY_NOT_FOUND Ein Artikel ist unbekannt oder nicht online sichtbar.
400 INVALID_OPTION Option unbekannt, mehrfach gewählt, oder in einer Einzelwahl-Gruppe mehr als eine.
400 REQUIRED_OPTION_MISSING Für eine Pflichtgruppe wurde keine Option gewählt.
400 MIN_ORDER_AMOUNT Mindestbestellwert nicht erreicht.
400 DELIVERY_AREA_NOT_COVERED Die Lieferadresse liegt außerhalb der Liefergebiete.
400 DELIVERY_MODULE_REQUIRED Lieferung braucht das Modul Lieferservice.
400 PREPAYMENT_REQUIRED Der Betrieb verlangt Online-Zahlung (stripe oder paypal).
400 TIP_ONLINE_ONLY Trinkgeld nur mit Online-Zahlung.
400 AGE_VERIFICATION_REQUIRED Ein Artikel ist altersbeschränkt und nur vor Ort bestellbar, nicht online.
400 ONLINE_ORDER_DISABLED Online-Bestellung ist im Betrieb ausgeschaltet.
400 ONLINEORDER_MODULE_REQUIRED Das Modul Online-Bestellung fehlt in der Lizenz.
422 IDEMPOTENCY_KEY_REUSED Derselbe Idempotency-Key mit anderem Inhalt (innerhalb der Wiederholungs-Zwischenspeicherung, eine Stunde).
409 IDEMPOTENCY_KEY_REUSED Der Idempotency-Key gehört schon zu einer anderen Bestellung (nach Ablauf dieser Stunde).
409 EXTERNAL_REFERENCE_CONFLICT Dieselbe externalReference mit anderem Inhalt (nur mit Benutzer-Anmeldung, siehe Feld).

Bestell-Geheimnis sofort speichern

guestSecret steht nur in dieser Antwort. Wer es nicht speichert, kann die Bestellung später weder abfragen noch bezahlen noch stornieren (mit Benutzer-Anmeldung oder Partner-Schlüssel bleibt die Bestellung über die Listen erreichbar).

Beispiel

curl -sS -X POST "$API/online-checkout/create" -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(cat /proc/sys/kernel/random/uuid)" -d '{
  "orderType": 0, "customerName": "Erika Muster", "customerEmail": "erika@example.com", "customerPhone": "0123 456789",
  "items": [ { "articleId": "art_917c19d4a18a", "quantity": 1, "notes": "ohne Zwiebeln" } ],
  "paymentMethod": "cash" }' | jq -c '.data | {id, orderNumber, status, grandTotal, guestSecret}'

Gekürzt mit jq. Das vollständige Objekt steht unter OnlineOrderResponse.

{"id": "olord_648e7d3cfa87", "orderNumber": "ON-20261110-001", "status": 1, "grandTotal": 9.5, "guestSecret": "q7Xm2LkP9vRtY4cNw8ZsB3hJdF6aGeUo"}

Bestellung lesen

GET /api/v1/online-checkout/{id}

Liefert den aktuellen Stand einer Bestellung. Der Shop fragt damit für die Statusseite des Gastes ab (alle 10 bis 15 Sekunden genügen).

Anmeldung: Bestell-Geheimnis (guestSecret aus der Antwort von Bestellung anlegen) in der Kopfzeile X-Order-Secret. Ohne oder mit falschem Geheimnis antwortet die Kasse 404 ENTITY_NOT_FOUND, genau wie bei einer unbekannten Bestellung. Als Ausweichweg für Links gibt es den Abfrageparameter s; die Kopfzeile ist vorzuziehen, weil sie in keinem Zugriffsprotokoll landet. Ein angemeldeter Kassenbenutzer (Bearer) braucht das Geheimnis nicht; ein Partner-Schlüssel ersetzt es nicht.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Antwort 200

data: OnlineOrderResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Bestellung unbekannt, oder das Bestell-Geheimnis fehlt bzw. passt nicht.

Beispiel

curl -sS "$API/online-checkout/$BESTELLUNG" -H "X-Order-Secret: $GEHEIMNIS" | jq -c '.data | {id, status, isPaid, estimatedReadyTime}'
{"id": "olord_648e7d3cfa87", "status": 1, "isPaid": false, "estimatedReadyTime": null}

Online-Zahlung starten

POST /api/v1/online-checkout/{id}/init-payment

Startet die Zahlung bei Stripe oder PayPal. Stripe: die Antwort liefert clientSecret und publishableKey für Stripe.js im Browser des Gastes. PayPal: die Antwort liefert eine redirectUrl, zu der der Gast weitergeleitet wird. Nur für Bestellungen im Stand 1 oder 2, die noch nicht bezahlt sind.

Anmeldung: Bestell-Geheimnis (guestSecret aus der Antwort von Bestellung anlegen) in der Kopfzeile X-Order-Secret. Ohne oder mit falschem Geheimnis antwortet die Kasse 404 ENTITY_NOT_FOUND, genau wie bei einer unbekannten Bestellung. Als Ausweichweg für Links gibt es den Abfrageparameter s; die Kopfzeile ist vorzuziehen, weil sie in keinem Zugriffsprotokoll landet. Ein angemeldeter Kassenbenutzer (Bearer) braucht das Geheimnis nicht; ein Partner-Schlüssel ersetzt es nicht.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
provider Text nein stripe oder paypal. Ohne Angabe richtet sich der Dienst nach der Zahlart der Bestellung.
returnUrl Text bedingt PayPal: https-Adresse, auf die der Gast nach dem Bezahlen zurückkommt. Für PayPal Pflicht.
cancelUrl Text nein PayPal: Adresse bei Abbruch. Ohne Angabe returnUrl.

Antwort 200

data: OnlinePaymentInitResponse

Feld Typ Bedeutung
provider Text? stripe oder paypal.
clientSecret Text? Stripe: Client-Secret des Zahlungsvorgangs für Stripe.js. Bei PayPal leer.
publishableKey Text? Stripe: öffentlicher Schlüssel des Betriebs für Stripe.js.
paymentIntentId Text? Stripe: Kennung des Zahlungsvorgangs.
redirectUrl Text? PayPal: Adresse, zu der der Gast weitergeleitet wird.

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Bestellung unbekannt oder Geheimnis falsch.
400 ALREADY_PAID Die Bestellung ist schon bezahlt.
400 INVALID_STATUS Die Bestellung ist nicht mehr im Stand 1 oder 2.
400 STRIPE_NOT_CONFIGURED Der Betrieb hat Stripe nicht eingerichtet.
400 PAYPAL_NOT_ENABLED PayPal ist im Shop nicht eingeschaltet.
400 PAYPAL_NOT_CONFIGURED Der Betrieb hat PayPal nicht eingerichtet.
400 RETURN_URL_REQUIRED PayPal braucht returnUrl (absolute https-Adresse).

Die Beispielantwort entfällt: auf einem Betrieb ohne eingerichtetes Stripe antwortet der Aufruf 400 STRIPE_NOT_CONFIGURED.

Beispiel

curl -sS -X POST "$API/online-checkout/$BESTELLUNG/init-payment" -H "X-Order-Secret: $GEHEIMNIS" \
  -H 'Content-Type: application/json' -d '{"provider":"stripe"}' | jq -c '.data | {provider, publishableKey, clientSecret}'

Zahlung prüfen

POST /api/v1/online-checkout/{id}/verify-payment

Prüft die Zahlung serverseitig bei Stripe bzw. PayPal und markiert die Bestellung erst dann als bezahlt (isPaid). Der Shop ruft ihn nach der Rückkehr des Gastes auf. Eine noch nicht bezahlte Bestellung kommt unverändert zurück.

Anmeldung: Bestell-Geheimnis (guestSecret aus der Antwort von Bestellung anlegen) in der Kopfzeile X-Order-Secret. Ohne oder mit falschem Geheimnis antwortet die Kasse 404 ENTITY_NOT_FOUND, genau wie bei einer unbekannten Bestellung. Als Ausweichweg für Links gibt es den Abfrageparameter s; die Kopfzeile ist vorzuziehen, weil sie in keinem Zugriffsprotokoll landet. Ein angemeldeter Kassenbenutzer (Bearer) braucht das Geheimnis nicht; ein Partner-Schlüssel ersetzt es nicht.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Antwort 200

data: OnlineOrderResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Bestellung unbekannt, oder das Bestell-Geheimnis fehlt bzw. passt nicht.

Beispiel

curl -sS -X POST "$API/online-checkout/$BESTELLUNG/verify-payment" -H "X-Order-Secret: $GEHEIMNIS" | jq -c '.data | {status, isPaid, amountDue}'

Noch nicht bezahlt: die Bestellung kommt unverändert zurück. Nach erfolgreicher Online-Zahlung steht isPaid auf true und amountDue auf 0.

{"status": 1, "isPaid": false, "amountDue": 9.5}

Bestellung stornieren

POST /api/v1/online-checkout/{id}/cancel

Storniert eine Bestellung, solange der Betrieb sie noch nicht angenommen hat (Stand unter 4). War sie schon online bezahlt, storniert die Kasse fiskalisch und erstattet den Betrag über den Zahlungsdienst.

Anmeldung: Bestell-Geheimnis (guestSecret aus der Antwort von Bestellung anlegen) in der Kopfzeile X-Order-Secret. Ohne oder mit falschem Geheimnis antwortet die Kasse 404 ENTITY_NOT_FOUND, genau wie bei einer unbekannten Bestellung. Als Ausweichweg für Links gibt es den Abfrageparameter s; die Kopfzeile ist vorzuziehen, weil sie in keinem Zugriffsprotokoll landet. Ein angemeldeter Kassenbenutzer (Bearer) braucht das Geheimnis nicht; ein Partner-Schlüssel ersetzt es nicht.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Antwort 200

data: OnlineOrderResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Bestellung unbekannt oder Geheimnis falsch.
400 CANNOT_CANCEL Der Betrieb hat die Bestellung schon angenommen; nur noch der Betrieb kann sie ablehnen.

Beispiel

curl -sS -X POST "$API/online-checkout/$BESTELLUNG/cancel" -H "X-Order-Secret: $GEHEIMNIS" | jq -c '.data | {id, status}'
{"id": "olord_87f30781b23f", "status": 9}

Offene Bestellungen lesen

GET /api/v1/online-checkout/pending

Die Bestellungen, die der Betrieb noch bearbeiten muss.

Anmeldung: Partner-Schlüssel mit dem Bereich orders:read in der Kopfzeile X-Dikas-Api-Key, oder eine Benutzer-Anmeldung (Bearer-Token). Ein Schlüssel ohne diesen Bereich wird mit 403 abgewiesen, ein ungültiger, abgelaufener oder widerrufener mit 401.

Antwort 200

data: Liste von OnlineOrderResponse?

Beispiel

curl -sS "$API/online-checkout/pending" -H "X-Dikas-Api-Key: $PARTNER_SCHLUESSEL" | jq -c '.data[] | {id, orderNumber, status, grandTotal}'
{"id": "olord_648e7d3cfa87", "orderNumber": "ON-20261110-001", "status": 1, "grandTotal": 9.5}

Heutige Bestellungen lesen

GET /api/v1/online-checkout/today

Alle Online-Bestellungen des heutigen Tages, in jedem Stand. Die Antwort enthält den Namen und die Kontaktdaten des Gastes; sie sind nur im Rahmen der Bestellabwicklung zu verwenden.

Anmeldung: Partner-Schlüssel mit dem Bereich orders:read in der Kopfzeile X-Dikas-Api-Key, oder eine Benutzer-Anmeldung (Bearer-Token). Ein Schlüssel ohne diesen Bereich wird mit 403 abgewiesen, ein ungültiger, abgelaufener oder widerrufener mit 401.

Antwort 200

data: Liste von OnlineOrderResponse?

Beispiel

curl -sS "$API/online-checkout/today" -H "X-Dikas-Api-Key: $PARTNER_SCHLUESSEL" | jq -c '[.data[] | {id, status, isPaid}]'
[{"id": "olord_648e7d3cfa87", "status": 1, "isPaid": false}]

Bestellung annehmen

POST /api/v1/online-checkout/{id}/confirm

Nimmt eine Bestellung an: Die Kasse legt den Lieferauftrag an, erzeugt die Küchenbons und druckt sie bzw. zeigt sie auf dem Küchenmonitor, und der Gast bekommt seine Bestätigung. Ein zweiter Aufruf ändert nichts und liefert die Bestellung unverändert zurück. Zum Annehmen nicht den Status 4 setzen: erst confirm löst Bons und Auftrag aus.

Anmeldung: Partner-Schlüssel mit dem Bereich orders:manage in der Kopfzeile X-Dikas-Api-Key, oder eine Benutzer-Anmeldung (Bearer-Token). Ein Schlüssel ohne diesen Bereich wird mit 403 abgewiesen, ein ungültiger, abgelaufener oder widerrufener mit 401.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).
estimatedMinutes Abfrage Ganzzahl nein Zugesagte Dauer in Minuten bis zur Fertigstellung.

Antwort 200

data: OnlineOrderResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Bestellung unbekannt.

Beispiel

curl -sS -X POST "$API/online-checkout/$BESTELLUNG/confirm?estimatedMinutes=20" -H "X-Dikas-Api-Key: $PARTNER_SCHLUESSEL" | jq -c '.data | {id, status}'
{"id": "olord_648e7d3cfa87", "status": 4}

Bestellung ablehnen

POST /api/v1/online-checkout/{id}/reject

Lehnt eine Bestellung ab. War sie schon online bezahlt, storniert die Kasse fiskalisch und erstattet den Betrag über den Zahlungsdienst. Ein zweiter Aufruf ändert nichts.

Anmeldung: Partner-Schlüssel mit dem Bereich orders:manage in der Kopfzeile X-Dikas-Api-Key, oder eine Benutzer-Anmeldung (Bearer-Token). Ein Schlüssel ohne diesen Bereich wird mit 403 abgewiesen, ein ungültiger, abgelaufener oder widerrufener mit 401.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
reason Text nein Grund, wird in rejectionReason der Bestellung gespeichert. Nicht erzwungen, aber zu empfehlen: der Betrieb sieht ihn.

Antwort 200

data: OnlineOrderResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Bestellung unbekannt.

Beispiel

curl -sS -X POST "$API/online-checkout/$BESTELLUNG/reject" -H "X-Dikas-Api-Key: $PARTNER_SCHLUESSEL" \
  -H 'Content-Type: application/json' -d '{"reason":"Heute geschlossen"}' | jq -c '.data | {id, status, rejectionReason}'
{"id": "olord_648e7d3cfa87", "status": 10, "rejectionReason": "Heute geschlossen"}

Bestellstatus setzen

PUT /api/v1/online-checkout/{id}/status

Setzt den Stand einer Bestellung (zum Beispiel 5 in Zubereitung, 6 bereit, 8 abgeschlossen) und benachrichtigt den Gast bei „bereit“ und „unterwegs“. Der Aufruf ändert nur den Stand: Bons und Aufträge legen confirm und die Kasse an. Eine feste Reihenfolge der Stände wird nicht erzwungen. Die Zuständigkeit klären Shop und Betrieb vorab: führt der Betrieb den Ablauf im Lieferservice, setzt sich der Stand dort; ein zusätzliches Setzen aus dem Shop ist dann nicht nötig.

Anmeldung: Partner-Schlüssel mit dem Bereich orders:manage in der Kopfzeile X-Dikas-Api-Key, oder eine Benutzer-Anmeldung (Bearer-Token). Ein Schlüssel ohne diesen Bereich wird mit 403 abgewiesen, ein ungültiger, abgelaufener oder widerrufener mit 401.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
status Ganzzahl ja Neuer Stand, 1 bis 10 (Werte siehe status der Bestellung).
reason Text nein Grund; wird bei 9 und 10 als rejectionReason gespeichert.

Antwort 200

data: OnlineOrderResponse

Fehler

HTTP errorCode Bedeutung
400 VALIDATION_ERROR status liegt nicht zwischen 1 und 10.
404 ENTITY_NOT_FOUND Bestellung unbekannt.

Beispiel

curl -sS -X PUT "$API/online-checkout/$BESTELLUNG/status" -H "X-Dikas-Api-Key: $PARTNER_SCHLUESSEL" \
  -H 'Content-Type: application/json' -d '{"status":5}' | jq -c '.data | {id, status}'
{"id": "olord_648e7d3cfa87", "status": 5}

Artikel und Warengruppen

Pflegt der Shop die Karte, überträgt er Artikel und Warengruppen mit Benutzer-Anmeldung an die Kasse. Vorab festlegen, wer führend ist: Pflegt der Betrieb dieselben Artikel auch in der Kasse, laufen Preise auseinander. Die Warengruppen sind hierarchisch nummeriert: eine Hauptgruppe hat drei Ziffern (100), jede Untergruppe hängt zwei Ziffern an (10001). Artikel erscheinen nur in Untergruppen. Das Feld groupId am Artikel trägt die Kennung der Gruppe (artgrp_…), nicht die Gruppennummer.

Warengruppen lesen

GET /api/v1/articles/groups

Liefert die Warengruppen des Betriebs, auf Wunsch nur aktive oder die Untergruppen einer Hauptgruppe.

Anmeldung: Benutzer-Anmeldung: Authorization: Bearer <Token>, das Token aus POST /api/v1/auth/login. Es gelten die Rechte des Benutzers. Ein Partner-Schlüssel wird an diesem Aufruf nicht angenommen (401).

Parameter

Name Ort Typ Pflicht Bedeutung
activeOnly Abfrage Ja/Nein nein true (Vorgabe): deaktivierte Gruppen ausblenden.
parentGroupId Abfrage Text nein Nur Untergruppen dieser Hauptgruppe (Gruppennummer, 100).

Antwort 200

data: Liste von ArticleGroupResponse?

Beispiel

curl -sS "$API/articles/groups" -H "Authorization: Bearer $TOKEN" | jq -c '.data[] | {id, groupId, name, parentId}'
{"id": "artgrp_ad5736686512", "groupId": "100", "name": "Speisekarte", "parentId": null}
{"id": "artgrp_2d6002dced5a", "groupId": "10001", "name": "Pizza", "parentId": "100"}

Je Gruppe eine Zeile (jq -c).

{"id": "artgrp_672e5000927d", "groupId": "10002", "name": "Getränke", "parentId": "100"}

Warengruppe anlegen

POST /api/v1/articles/groups

Legt eine Warengruppe an. Die übergeordnete Gruppe ergibt sich aus der Nummer: 10001 gehört zu 100.

Anmeldung: Benutzer-Anmeldung: Authorization: Bearer <Token>, das Token aus POST /api/v1/auth/login. Es gelten die Rechte des Benutzers. Ein Partner-Schlüssel wird an diesem Aufruf nicht angenommen (401).

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
groupId Text ja Gruppennummer, nur Ziffern: Hauptgruppe 3 Ziffern, jede Untergruppe hängt zwei Ziffern an (100, 10001). Eine vorhandene Nummer ergibt DUPLICATE_GROUP_ID.
name Text ja Name.
shortName Text nein Kurzname.
description Text nein Beschreibung.
color Text nein Farbe (#C683CD); ohne Angabe wählt die Kasse eine.
sortOrder Ganzzahl nein Sortierwert.
isActive Ja/Nein nein Aktiv.

Antwort 201

data: ArticleGroupResponse

Fehler

HTTP errorCode Bedeutung
400 DUPLICATE_GROUP_ID Die Gruppennummer ist schon vergeben.
400 VALIDATION_ERROR Name oder Gruppennummer fehlen oder sind ungültig.

Beispiel

curl -sS -X POST "$API/articles/groups" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"groupId":"10097","name":"Pasta","sortOrder":3,"isActive":true}' | jq -c '.data | {id, groupId, name, parentId}'

Die Antwort hat den Status 201.

{"id": "artgrp_a86a901e51a4", "groupId": "10095", "name": "Pasta", "parentId": "100"}

Warengruppe ändern

PATCH /api/v1/articles/groups/{documentId}

Ändert einzelne Felder einer Warengruppe als JSON Patch (siehe Grundlagen). documentId ist die Kennung artgrp_…. Nicht änderbar sind unter anderem id, groupId und parentId.

Anmeldung: Benutzer-Anmeldung: Authorization: Bearer <Token>, das Token aus POST /api/v1/auth/login. Es gelten die Rechte des Benutzers. Ein Partner-Schlüssel wird an diesem Aufruf nicht angenommen (401).

Parameter

Name Ort Typ Pflicht Bedeutung
documentId Pfad Text ja Kennung der Warengruppe (artgrp_…).

Anfrage (application/json, application/json-patch+json)

Als Content-Type: application/json-patch+json. Beispiel: [{"op":"replace","path":"/sortOrder","value":4}].

Body: Liste von JsonPatchOperation

Antwort 200

data: ArticleGroupResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Warengruppe unbekannt.
409 PATCH_TEST_FAILED Eine test-Operation hat nicht gehalten: der Wert wurde inzwischen geändert; validationErrors nennt Pfad, erwarteten und gefundenen Wert. Es wird nichts geschrieben.
400 PATCH_FIELD_UNKNOWN Der Pfad ist unbekannt oder nicht schreibbar.
400 PATCH_FIELD_READONLY Das Feld wird von der Kasse berechnet und ist nicht schreibbar.
400 PATCH_INVALID Der Wert ist ungültig oder das Feld lässt sich nicht leeren.

Beispiel

curl -sS -X PATCH "$API/articles/groups/$GRUPPE" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json-patch+json' -d '[{"op":"replace","path":"/sortOrder","value":4}]' \
  | jq -c '.data | {id, name, sortOrder}'
{"id": "artgrp_a86a901e51a4", "name": "Pasta", "sortOrder": 4}

Artikel lesen

GET /api/v1/articles

Liefert Artikel, auf Wunsch einer Gruppe, mit Seitenweise-Abruf. Deaktivierte Artikel fehlen in der Voreinstellung.

Anmeldung: Benutzer-Anmeldung: Authorization: Bearer <Token>, das Token aus POST /api/v1/auth/login. Es gelten die Rechte des Benutzers. Ein Partner-Schlüssel wird an diesem Aufruf nicht angenommen (401).

Parameter

Name Ort Typ Pflicht Bedeutung
groupId Abfrage Text nein Nur Artikel dieser Gruppe (Kennung artgrp_…).
activeOnly Abfrage Ja/Nein nein true (Vorgabe): deaktivierte Artikel ausblenden.
limit Abfrage Ganzzahl nein Höchstzahl je Aufruf; ohne Angabe alle.
skip Abfrage Ganzzahl nein Wie viele Artikel am Anfang übersprungen werden.

Antwort 200

data: Liste von ArticleResponse?

Beispiel

curl -sS "$API/articles?groupId=$GRUPPE&limit=50" -H "Authorization: Bearer $TOKEN" | jq -c '.data[] | {id, name, price, groupId}'
{"id": "art_f6f95549ca21", "name": "Spaghetti Bolognese", "price": 13.5, "groupId": "artgrp_a86a901e51a4"}

Einen Artikel lesen

GET /api/v1/articles/{id}

Liefert einen Artikel. Das Feld version ist die Versionsmarke, die sich bei jeder Änderung ändert.

Anmeldung: Benutzer-Anmeldung: Authorization: Bearer <Token>, das Token aus POST /api/v1/auth/login. Es gelten die Rechte des Benutzers. Ein Partner-Schlüssel wird an diesem Aufruf nicht angenommen (401).

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Antwort 200

data: ArticleResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Artikel unbekannt.

Beispiel

curl -sS "$API/articles/$ARTIKEL" -H "Authorization: Bearer $TOKEN" | jq -c '.data | {id, name, price, taxClass, taxRate, manualSoldOut, hideFromMenu}'
{"id": "art_f6f95549ca21", "name": "Spaghetti Bolognese", "price": 12.5, "taxClass": 1, "taxRate": 7, "manualSoldOut": false, "hideFromMenu": false}

Artikel anlegen

POST /api/v1/articles

Legt einen Artikel an. Der Artikel erscheint in der Karte, sobald er aktiv ist, in einer Untergruppe liegt und nicht ausgeblendet ist. Optionen (Extras, Größen) und Allergene pflegt der Betrieb in der Kasse; der Shop liest sie aus der Karte.

Anmeldung: Benutzer-Anmeldung: Authorization: Bearer <Token>, das Token aus POST /api/v1/auth/login. Es gelten die Rechte des Benutzers. Ein Partner-Schlüssel wird an diesem Aufruf nicht angenommen (401).

Anfrage (application/json)

Die Anfrage kennt weitere Felder der Kasse (Lager, Rezepturen, Druckoptionen); für die Anbindung genügen die folgenden.

Feld Typ Pflicht Bedeutung
name Text ja Name des Artikels.
taxClass Ganzzahl bedingt Steuerart: 0 Normalsatz, 1 ermäßigt, 99 steuerfrei. Entweder taxClass oder taxRate ist nötig, sonst ARTIKEL_TYP_ERFORDERLICH.
taxRate Dezimalzahl bedingt Steuersatz in Prozent; wird auf die passende Steuerart abgebildet, wenn taxClass fehlt. Entweder taxClass oder taxRate ist nötig.
groupId Text nein Kennung der Warengruppe (artgrp_…) — eine Untergruppe; Artikel ohne Gruppe oder in Hauptgruppen erscheinen nicht in der Karte. Die Kasse prüft die Kennung nicht: eine unbekannte wird angenommen, der Artikel ist dann nirgends zugeordnet.
price Dezimalzahl nein Verkaufspreis; immer angeben.
articleNumber Text nein Artikelnummer.
shortName Text nein Kurzname.
description Text nein Beschreibung.
unit Text nein Einheit (Stück).
barcode Text nein Strichcode.
isActive Ja/Nein nein Aktiv; sonst weder Kasse noch Shop.
hideFromMenu Ja/Nein nein true: nicht im Shop, an der Kasse buchbar.
manualSoldOut Ja/Nein nein true: heute nicht verfügbar.
allergens Liste von Text nein Allergen-Kürzel.
sortOrder Ganzzahl nein Sortierwert.
pfand Dezimalzahl nein Pfand je Einheit.

Antwort 201

data: ArticleResponse

Fehler

HTTP errorCode Bedeutung
400 VALIDATION_ERROR Name fehlt oder ein Wert ist ungültig.
400 ARTIKEL_TYP_ERFORDERLICH Weder taxClass noch taxRate angegeben; die Kasse nimmt keinen Standardsatz an.
400 ARTICLE_NUMBER_TAKEN Die Artikelnummer ist schon vergeben.

Beispiel

curl -sS -X POST "$API/articles" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d "{
  \"articleNumber\":\"2101\", \"name\":\"Spaghetti Bolognese\", \"description\":\"Hackfleischsauce, Parmesan\",
  \"groupId\":\"$GRUPPE\", \"price\":12.5, \"taxRate\":7, \"isActive\":true }" | jq -c '.data | {id, name, price}'

Die Antwort hat den Status 201.

{"id": "art_f6f95549ca21", "name": "Spaghetti Bolognese", "price": 12.5}

Artikel ändern

PATCH /api/v1/articles/{id}

Ändert einzelne Felder eines Artikels als JSON Patch (siehe Grundlagen): nur die genannten Felder werden geschrieben, alle anderen bleiben, auch wenn der Betrieb sie inzwischen in der Kasse geändert hat. Jede Änderung sollte eine test-Operation auf den Ausgangswert tragen. Ein Feld auf null zu setzen leert es; Name, Preis und Haken lassen sich nicht leeren. Von der Kasse berechnete Felder (taxRate, Zeitstempel) lassen sich nicht schreiben.

Anmeldung: Benutzer-Anmeldung: Authorization: Bearer <Token>, das Token aus POST /api/v1/auth/login. Es gelten die Rechte des Benutzers. Ein Partner-Schlüssel wird an diesem Aufruf nicht angenommen (401).

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Anfrage (application/json, application/json-patch+json)

Als Content-Type: application/json-patch+json. Häufige Pfade: /price (Preis), /manualSoldOut (heute nicht verfügbar), /hideFromMenu (nicht im Shop), /isActive (ganz abschalten), /name, /description.

Body: Liste von JsonPatchOperation

Antwort 200

data: ArticleResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Artikel unbekannt.
409 PATCH_TEST_FAILED Eine test-Operation hat nicht gehalten; es wird nichts geschrieben.
400 PATCH_FIELD_UNKNOWN Der Pfad ist unbekannt oder nicht schreibbar.
400 PATCH_FIELD_READONLY Das Feld wird von der Kasse berechnet (taxRate, Zeitstempel).
400 PATCH_INVALID Der Wert ist ungültig oder das Feld lässt sich nicht leeren.

Beispiel

curl -sS -X PATCH "$API/articles/$ARTIKEL" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json-patch+json' \
  -d '[{"op":"test","path":"/price","value":12.5},{"op":"replace","path":"/price","value":13.5}]' \
  | jq -c '.data | {id, price, version}'
{"id": "art_f6f95549ca21", "price": 13.5, "version": "2-77c79e0ba0799b37b89560951d391b8c"}

Hat sich der Preis inzwischen geändert, antwortet die Kasse mit 409 und schreibt nichts:

{"success": false, "data": null, "errorCode": "PATCH_TEST_FAILED", "errorMessage": "Zwischenzeitlich geaendert: /price. Bitte neu laden.", "validationErrors": [{"field": "/price", "message": "erwartet: 1, gefunden: 9.5"}]}

Ereignis-Feed

Der Feed liefert dieselben Ereignisse, die auch an ein Webhook-Ziel gesendet werden. Er ist der Weg ohne eigene öffentliche Adresse und die Rückfallebene, wenn ein Webhook-Empfänger ausfiel. Die Ereignisse enthalten bewusst keine Namen, Adressen oder Positionen, nur Kennungen und Stand; Einzelheiten holt der Shop mit seinem Schlüssel (orders:read). Zustellung mindestens einmal: Empfänger entdoppeln über id und ordnen über seq. Ereignisse werden 14 Tage aufbewahrt.

Ereignisse lesen

GET /api/v1/events

Liefert die Ereignisse nach einem Zeiger. Ohne after kommen die der letzten 24 Stunden. Die Antwort nennt in nextCursor den Zeiger für den nächsten Aufruf, auch wenn es nichts Neues gab. Ein Ereignis ist erst zwei Sekunden nach seiner Entstehung sichtbar.

Anmeldung: Partner-Schlüssel mit dem Bereich events:read in der Kopfzeile X-Dikas-Api-Key, oder eine Benutzer-Anmeldung (Bearer-Token). Ein Schlüssel ohne diesen Bereich wird mit 403 abgewiesen, ein ungültiger, abgelaufener oder widerrufener mit 401.

Parameter

Name Ort Typ Pflicht Bedeutung
after Abfrage Text nein Zeiger aus nextCursor der letzten Antwort.
limit Abfrage Ganzzahl nein Größe der Seite, 1 bis 500; Vorgabe 100.

Antwort 200

data: WebhookEventsPageResponse

Feld Typ Bedeutung
events Liste von WebhookEventResponse? Die Ereignisse, aufsteigend nach Entstehung.
nextCursor Text? Zeiger für den nächsten Aufruf (after); immer gesetzt, auch ohne neue Ereignisse.
hasMore Ja/Nein true: weitere Ereignisse liegen sofort bereit.

Enthaltene Datenobjekte: WebhookEventResponse.

Fehler

HTTP errorCode Bedeutung
400 INVALID_CURSOR Der Zeiger after ist ungültig; den Wert nextCursor der letzten Antwort verwenden.

Der Zeiger ist ein undurchsichtiger Text (c1.3.2026111017): nur weitergeben, nicht auswerten. Den zuletzt erhaltenen nextCursor speichern und beim nächsten Aufruf als after senden.

Beispiel

curl -sS "$API/events?after=$ZEIGER&limit=100" -H "X-Dikas-Api-Key: $PARTNER_SCHLUESSEL" | jq '.data'

Gekürzt auf zwei Ereignisse.

{
  "events": [
    {
      "id": "olord_4c1c808725b8.1",
      "type": "order.created",
      "occurredAt": "2026-11-10T17:45:30Z",
      "tenant": "pizzeria",
      "seq": 1,
      "apiVersion": "2026-11-01",
      "data": {
        "orderId": "olord_4c1c808725b8",
        "orderNumber": "ON-20261110-006",
        "orderType": 0
      }
    },
    {
      "id": "olord_4c1c808725b8.2",
      "type": "order.status_changed",
      "occurredAt": "2026-11-10T17:45:30Z",
      "tenant": "pizzeria",
      "seq": 2,
      "apiVersion": "2026-11-01",
      "data": {
        "orderId": "olord_4c1c808725b8",
        "orderNumber": "ON-20261110-006",
        "status": 4,
        "previousStatus": 1,
        "isPaid": false
      }
    }
  ],
  "nextCursor": "c1.3.2026111017",
  "hasMore": false
}

Webhook-Ziele

Ein Administrator des Betriebs legt fest, an welche https-Adressen die Kasse Ereignisse sendet (höchstens drei Ziele je Betrieb). Die Zustellung ist signiert und wird bei Fehlern über Stunden wiederholt. Signatur, Ereignisarten und Testvektor stehen im Leitfaden Eigenen Webshop anbinden. Das Geheimnis eines Ziels (whsec_…) steht nur in der Antwort von Anlegen und Erneuern.

Webhook-Ziele lesen

GET /api/v1/webhook-targets

Liefert alle Ziele des Betriebs, ohne Geheimnis.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Antwort 200

data: Liste von WebhookTargetResponse

Beispiel

curl -sS "$API/webhook-targets" -H "Authorization: Bearer $ADMIN_TOKEN" | jq -c '.data[] | {id, name, url, enabled}'
{"id": "whz_0123456789abcdef", "name": "Webshop", "url": "https://shop.example.de/dikas/ereignisse", "enabled": true}

Webhook-Ziel anlegen

POST /api/v1/webhook-targets

Legt ein Ziel an. Die Antwort enthält das Geheimnis zur Signaturprüfung genau dieses eine Mal.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
name Text ja Bezeichnung, höchstens 120 Zeichen.
url Text ja https-Adresse, Port 443, öffentlich erreichbar, ohne Zugangsdaten in der Adresse.
eventTypes Liste von Text nein Gewünschte Ereignisarten (order.created, order.status_changed, payment.received, order.cancelled, ticket.ready); ohne Angabe alle.

Antwort 201

data: WebhookTargetCreatedResponse

Fehler

HTTP errorCode Bedeutung
400 WEBHOOK_TARGET_REJECTED Die Adresse wird abgelehnt: nicht https, nicht Port 443, nicht öffentlich auflösbar, interne oder private Adresse, oder Zugangsdaten in der Adresse. errorMessage nennt den Grund.
400 WEBHOOK_TARGET_LIMIT Es gibt schon drei Ziele.
400 INVALID_NAME Name fehlt oder ist zu lang.
400 INVALID_EVENT_TYPE Eine Ereignisart ist unbekannt.

Geheimnis sofort speichern

Das Geheimnis lässt sich nicht erneut abrufen. Ist es verloren, hilft nur Geheimnis erneuern.

Beispiel

curl -sS -X POST "$API/webhook-targets" -H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"Webshop","url":"https://shop.example.de/dikas/ereignisse"}' | jq '.data | {id, secret, eventTypes, enabled}'

Die Antwort hat den Status 201. Leere eventTypes heißt: alle Ereignisarten. (Das Geheimnis hier ist ein Platzhalter.)

{
  "id": "whz_0123456789abcdef",
  "secret": "whsec_Zm9vYmFyLWJlaXNwaWVsLW5pY2h0LWVjaHQtMDAwMQ",
  "eventTypes": [],
  "enabled": true
}

Webhook-Ziel ändern

PATCH /api/v1/webhook-targets/{id}

Ändert Name, Adresse, Ereignisarten oder den Schalter. Fehlende Felder bleiben unverändert. Eine neue Adresse wird wie beim Anlegen geprüft.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
name Text nein Neue Bezeichnung.
url Text nein Neue Adresse; wird wie beim Anlegen geprüft.
eventTypes Liste von Text nein Neue Ereignisarten.
enabled Ja/Nein nein Ziel ein- oder ausschalten.

Antwort 200

data: WebhookTargetResponse

Fehler

HTTP errorCode Bedeutung
400 WEBHOOK_TARGET_REJECTED Die Adresse wird abgelehnt: nicht https, nicht Port 443, nicht öffentlich auflösbar, interne oder private Adresse, oder Zugangsdaten in der Adresse. errorMessage nennt den Grund.
404 ENTITY_NOT_FOUND Ziel unbekannt.
400 INVALID_EVENT_TYPE Eine Ereignisart ist unbekannt.

Beispiel

curl -sS -X PATCH "$API/webhook-targets/$ZIEL" -H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' \
  -d '{"eventTypes":["order.status_changed"],"enabled":true}' | jq -c '.data | {id, eventTypes, enabled}'
{"id": "whz_0123456789abcdef", "eventTypes": ["order.status_changed"], "enabled": true}

Webhook-Geheimnis erneuern

POST /api/v1/webhook-targets/{id}/rotate-secret

Erzeugt ein neues Geheimnis (steht in der Antwort). Das alte gilt für die Übergangsfrist daneben weiter; in dieser Zeit stehen beide Signaturen im Kopf der Zustellung.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
graceDays Ganzzahl nein Wie viele Tage das alte Geheimnis daneben gilt, 1 bis 30; ohne Angabe 7.

Antwort 200

data: WebhookTargetCreatedResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Ziel unbekannt.
400 INVALID_GRACE graceDays liegt nicht zwischen 1 und 30.

Beispiel

curl -sS -X POST "$API/webhook-targets/$ZIEL/rotate-secret" -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' -d '{"graceDays":7}' | jq '.data | {id, secret, previousSecretValidUntil}'

Das Geheimnis hier ist ein Platzhalter.

{
  "id": "whz_0123456789abcdef",
  "secret": "whsec_Zm9vYmFyLWJlaXNwaWVsLW5pY2h0LWVjaHQtMDAwMQ",
  "previousSecretValidUntil": "2026-11-17T17:45:01.9995529Z"
}

Webhook-Ziel löschen

DELETE /api/v1/webhook-targets/{id}

Löscht ein Ziel. Noch offene Zustellungen werden als tot sichtbar; die Ereignisse bleiben im Feed.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Antwort 204

Ohne Inhalt.

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Ziel unbekannt (auch bei einem zweiten Aufruf).

Beispiel

curl -sS -X DELETE "$API/webhook-targets/$ZIEL" -H "Authorization: Bearer $ADMIN_TOKEN" -w '%{http_code}\n'

Ausgabe: 204, ohne Inhalt.

204

Zustellungen lesen

GET /api/v1/webhook-targets/deliveries

Zeigt die Zustellungen der Ereignisse an die Ziele: wann zuletzt versucht, mit welchem Fehler. Ohne status die offenen und die toten. Ein Fehlertext enthält nie den Rumpf der Gegenseite.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Parameter

Name Ort Typ Pflicht Bedeutung
status Abfrage Text nein Offen, Erledigt oder Tot.
limit Abfrage Ganzzahl nein Höchstens 500; Vorgabe 100.

Antwort 200

data: Liste von WebhookDeliveryResponse

Feld Typ Bedeutung
id Text Kennung der Zustellung.
eventId Text Kennung des Ereignisses.
targetId Text Kennung des Ziels.
status Text Offen, Erledigt oder Tot.
attempts Ganzzahl Bisherige Versuche.
nextAttemptAt Zeitpunkt Nächster Versuch.
lastError Text? Fehlercode des letzten Versuchs (HTTP_500, TIMEOUT, TLS); nie ein Rumpf.
lastHttpStatus Ganzzahl? HTTP-Status der letzten Antwort des Empfängers.
deliveredAt Zeitpunkt? Zugestellt am.
createdDate Zeitpunkt Angelegt am.

Fehler

HTTP errorCode Bedeutung
400 INVALID_STATUS status ist nicht Offen, Erledigt oder Tot.

Beispiel

curl -sS "$API/webhook-targets/deliveries?status=Tot" -H "Authorization: Bearer $ADMIN_TOKEN" | jq -c '.data[] | {eventId, status, attempts, lastError}'

Tote Zustellungen wiederholen

POST /api/v1/webhook-targets/deliveries/retry

Sendet tote Zustellungen erneut, mit derselben Ereignis-Kennung. Ohne ids alle toten. Die Antwort nennt die Zahl der zurückgesetzten Zustellungen.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
ids Liste von Text nein Kennungen der Zustellungen; ohne Angabe alle toten.

Antwort 200

data: Ganzzahl

Beispiel

curl -sS -X POST "$API/webhook-targets/deliveries/retry" -H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' -d '{}' | jq .data

Ohne tote Zustellungen: 0.

0

Partner-Schlüssel

Ein Partner-Schlüssel ist der Server-zu-Server-Zugang eines Shops. Er gehört zu einem Betrieb und zu einem technischen Benutzer (aktiv, weder Administrator noch Manager), dessen Rechte zusätzlich gelten. Er wird in der Kopfzeile X-Dikas-Api-Key gesendet (dk1_<Kennung>.<Geheimnis>), nicht als Authorization: Bearer, und gilt nur an Aufrufen, die einen seiner Bereiche nennen. Schlüssel, Benutzer und Einstellungen erreicht er nie. Die Verwaltung ist Administratoren vorbehalten und geht auch in Einstellungen → System → Partner-Schlüssel.

Bereiche lesen

GET /api/v1/partner-api-keys/scopes

Die zulässigen Bereiche und der Name der Kopfzeile.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Antwort 200

data: PartnerApiScopesResponse

Feld Typ Bedeutung
scopes Liste von Text Alle Bereiche.
header Text Name der Kopfzeile, in der der Schlüssel gesendet wird.

Beispiel

curl -sS "$API/partner-api-keys/scopes" -H "Authorization: Bearer $ADMIN_TOKEN" | jq -c .data

Bedeutung der Bereiche: orders:read Bestellungen lesen (pending, today), orders:manage annehmen, ablehnen und Status setzen, tables:read Tische lesen, events:read den Ereignis-Feed lesen.

{"scopes": ["orders:read", "orders:manage", "tables:read", "events:read"], "header": "X-Dikas-Api-Key"}

Partner-Schlüssel lesen

GET /api/v1/partner-api-keys

Alle Schlüssel des Betriebs, ohne Geheimnis. status ist active, expired oder revoked.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Antwort 200

data: Liste von PartnerApiKeyResponse

Beispiel

curl -sS "$API/partner-api-keys" -H "Authorization: Bearer $ADMIN_TOKEN" | jq -c '.data[] | {id, keyId, name, scopes, status}'
{"id": "pak_0123456789abcdef", "keyId": "0123456789abcdef", "name": "Webshop", "scopes": ["orders:read", "orders:manage", "events:read"], "status": "active"}

Partner-Schlüssel anlegen

POST /api/v1/partner-api-keys

Legt einen Schlüssel an. Die Antwort enthält den vollständigen Schlüssel (key) genau dieses eine Mal. Der Schlüssel läuft nach expiresInDays ab (Vorgabe 365, höchstens 400); höchstens 50 gültige Schlüssel je Betrieb.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
name Text ja Bezeichnung, höchstens 120 Zeichen, zum Beispiel der Name des Shops.
scopes Liste von Text ja Erlaubte Bereiche, mindestens einer (orders:read, orders:manage, tables:read, events:read).
apiUserId Text ja Kennung des technischen Benutzers, dessen Kassenrechte zusätzlich gelten. Kein Administrator, kein Manager.
expiresInDays Ganzzahl nein Gültigkeit in Tagen, 1 bis 400; ohne Angabe 365.

Antwort 201

data: PartnerApiKeyCreatedResponse

Fehler

HTTP errorCode Bedeutung
400 API_USER_INVALID Der technische Benutzer existiert nicht oder ist gesperrt.
400 API_USER_PRIVILEGED Der technische Benutzer ist Administrator oder Manager.
400 PARTNER_KEY_LIMIT Es gibt schon 50 gültige Schlüssel.
400 VALIDATION_ERROR Name, Bereich oder Gültigkeit fehlen oder sind ungültig.
403 FORBIDDEN Kein Administrator.

Schlüssel sofort speichern

Der Schlüssel wird nicht gespeichert und nie wieder angezeigt. Ist er verloren, hilft nur Erneuern oder ein neuer Schlüssel.

Beispiel

curl -sS -X POST "$API/partner-api-keys" -H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' \
  -d "{\"name\":\"Webshop\",\"scopes\":[\"orders:read\",\"orders:manage\",\"events:read\"],\"apiUserId\":\"$BENUTZER\",\"expiresInDays\":365}" \
  | jq '.data | {id, key, scopes, expiresAt, status}'

Die Antwort hat den Status 201. Der Schlüssel hier ist ein Platzhalter.

{
  "id": "pak_0123456789abcdef",
  "key": "dk1_0123456789abcdef.Zm9vYmFyLWJlaXNwaWVsLXNjaGx1ZXNzZWwtMDAwMQ",
  "scopes": [
    "orders:read",
    "orders:manage",
    "events:read"
  ],
  "expiresAt": "2027-11-10T17:45:29.775954Z",
  "status": "active"
}

Partner-Schlüssel erneuern

POST /api/v1/partner-api-keys/{id}/rotate

Erneuert den Schlüssel: gleiche Kennung, neues Geheimnis (steht in der Antwort). Das alte gilt noch 24 Stunden (previousValidUntil), damit der Shop ohne Unterbrechung umstellen kann.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Anfrage (application/json)

Feld Typ Pflicht Bedeutung
expiresInDays Ganzzahl nein Neue Gültigkeit in Tagen, 1 bis 400; ohne Angabe 365.

Antwort 200

data: PartnerApiKeyCreatedResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Schlüssel unbekannt.
400 PARTNER_KEY_REVOKED Ein widerrufener Schlüssel lässt sich nicht erneuern.

Beispiel

curl -sS -X POST "$API/partner-api-keys/$SCHLUESSEL_ID/rotate" -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' -d '{"expiresInDays":365}' | jq '.data | {id, key, previousValidUntil}'

Der Schlüssel hier ist ein Platzhalter.

{
  "id": "pak_0123456789abcdef",
  "key": "dk1_0123456789abcdef.Zm9vYmFyLWJlaXNwaWVsLXNjaGx1ZXNzZWwtMDAwMQ",
  "previousValidUntil": "2026-11-11T17:45:34.1449777Z"
}

Partner-Schlüssel widerrufen

DELETE /api/v1/partner-api-keys/{id}

Widerruft den Schlüssel. Das wirkt mit der nächsten Anfrage; auch ein noch gültiges altes Geheimnis ist danach ungültig.

Anmeldung: Benutzer-Anmeldung (Bearer) eines Administrators. Ein Partner-Schlüssel erreicht diesen Aufruf nie; ein Benutzer ohne Administratorrolle erhält 403.

Parameter

Name Ort Typ Pflicht Bedeutung
id Pfad Text ja Kennung des Objekts, wie sie die Kasse in früheren Antworten genannt hat (olord_…, art_…, whz_…, pak_…).

Antwort 200

data: PartnerApiKeyResponse

Fehler

HTTP errorCode Bedeutung
404 ENTITY_NOT_FOUND Schlüssel unbekannt.

Beispiel

curl -sS -X DELETE "$API/partner-api-keys/$SCHLUESSEL_ID" -H "Authorization: Bearer $ADMIN_TOKEN" | jq -c '.data | {id, status, revokedAt}'
{"id": "pak_0123456789abcdef", "status": "revoked", "revokedAt": "2026-11-10T17:45:34.3263976Z"}

Datenobjekte

Die Datenobjekte, auf die die Aufrufe oben verweisen. Felder, die hier nicht stehen, sind für die Anbindung nicht vorgesehen und können sich ändern.

ArticleGroupResponse

Eine Warengruppe.

Feld Typ Bedeutung
id Text? Kennung (artgrp_…); sie gehört in groupId am Artikel.
groupId Text? Gruppennummer: Hauptgruppe 3 Ziffern (100), jede Untergruppe hängt zwei Ziffern an (10001).
name Text? Name.
shortName Text? Kurzname.
description Text? Beschreibung.
parentId Text? Gruppennummer der übergeordneten Gruppe; leer bei Hauptgruppen.
color Text? Farbe.
sortOrder Ganzzahl Sortierwert.
isActive Ja/Nein Aktiv.
version Text? Versionsmarke der Kasse.

ArticleResponse

Ein Artikel der Kasse (die für Webshops wichtigen Felder).

Feld Typ Bedeutung
id Text? Kennung (art_…).
version Text? Versionsmarke der Kasse, ändert sich bei jeder Änderung.
articleNumber Text? Artikelnummer.
name Text? Name.
shortName Text? Kurzname.
description Text? Beschreibung.
groupId Text? Kennung der Warengruppe (artgrp_…), nicht die Gruppennummer.
price Dezimalzahl Verkaufspreis.
taxClass Ganzzahl? Steuerart: 0 Normalsatz, 1 ermäßigt, 99 steuerfrei.
taxRate Dezimalzahl? Steuersatz in Prozent; von der Kasse aus taxClass bestimmt und nicht schreibbar.
unit Text? Einheit (Stück).
barcode Text? Strichcode.
isActive Ja/Nein false: weder Kasse noch Shop.
hideFromMenu Ja/Nein true: nicht im Shop, an der Kasse buchbar.
manualSoldOut Ja/Nein true: heute nicht verfügbar (der „86er“-Schalter).
sortOrder Ganzzahl Sortierwert.
allergens Liste von Text? Allergen-Kürzel.
pfand Dezimalzahl Pfand je Einheit.

ArticleWebImage

Ein Bild eines Artikels.

Feld Typ Bedeutung
attachmentName Text? Dateiname des Bildes, für Artikelbild laden.
thumbnailName Text? Dateiname der kleinen Ansicht.
caption Text? Bildunterschrift.
sortOrder Ganzzahl Reihenfolge.
isPrimary Ja/Nein Hauptbild des Artikels.

JsonPatchOperation

Eine Operation nach RFC 6902.

Feld Typ Bedeutung
op Text? replace, add, remove oder test.
path Text? Pfad des Feldes mit führendem Schrägstrich (/price).
value beliebig Neuer bzw. erwarteter Wert (beliebiger JSON-Wert); bei remove entfällt er.

OnlineMenuAllergen

Ein Allergen-Kürzel mit Klartext.

Feld Typ Bedeutung
code Text? Kürzel, wie es an den Artikeln steht (A).
name Text? Bezeichnung (Glutenhaltiges Getreide).

OnlineMenuGroup

Eine Warengruppe der Karte.

Feld Typ Bedeutung
id Text? Kennung der Gruppe (artgrp_…).
name Text? Name der Gruppe.
color Text? Farbe der Gruppe (#C683CD), falls gepflegt.
sortOrder Ganzzahl Sortierwert; kleinere Werte zuerst.
parentName Text? Name der Hauptgruppe, zu der die Gruppe gehört.
articles Liste von OnlineMenuItem? Die Artikel der Gruppe.

OnlineMenuItem

Ein Artikel der Karte.

Feld Typ Bedeutung
id Text? Kennung des Artikels (art_…); gehört in items[].articleId beim Bestellen.
name Text? Name.
description Text? Kurzbeschreibung.
price Dezimalzahl Verkaufspreis je Einheit in der Währung des Betriebs, ohne Optionen und ohne Pfand.
pfand Dezimalzahl Pfand je Einheit, 0 ohne Pfand. Die Kasse fügt es beim Bestellen als eigene Position hinzu; es ist nicht in price enthalten.
webTitle Text? Abweichender Titel für den Shop, falls gepflegt.
longDescription Text? Ausführliche Beschreibung.
ingredients Text? Zutaten (Freitext).
spicyLevel Ganzzahl Schärfegrad (0 = nicht scharf).
isVegan Ja/Nein Vegan.
isVegetarian Ja/Nein Vegetarisch.
isGlutenFree Ja/Nein Glutenfrei.
preparationTimeMinutes Ganzzahl? Zubereitungsdauer in Minuten, falls gepflegt.
soldOut Ja/Nein true: heute nicht verfügbar. Der Artikel steht weiter auf der Karte, wird beim Bestellen aber mit ARTICLE_SOLD_OUT abgewiesen.
allergens Liste von Text? Allergen-Kürzel; Klartext in allergenDefinitions.
additives Liste von Text? Zusatzstoffe (Freitext).
images Liste von ArticleWebImage? Bilder des Artikels; abrufbar über Artikelbild laden.
options Liste von OnlineMenuOptionGroup? Wählbare Optionen (Extras, Größen) in Gruppen.

OnlineMenuOption

Eine wählbare Option.

Feld Typ Bedeutung
name Text? Name; er dient beim Bestellen zur Auswahl (selectedOptions[].name).
price Dezimalzahl Aufpreis. Bei isPercent ein Prozentsatz vom Artikelpreis (10 = 10 %), sonst ein Geldbetrag.
isPercent Ja/Nein true: price ist ein Prozentsatz.
isDefault Ja/Nein Vorgewählt.
appMode Ganzzahl 0 normal, 1 Standard (vorgewählt), 2 im Preis enthalten (kein Aufschlag).
soldOut Ja/Nein true: die Option ist heute nicht verfügbar.

OnlineMenuOptionGroup

Eine Gruppe wählbarer Optionen eines Artikels.

Feld Typ Bedeutung
id Text? Kennung der Gruppe.
caption Text? Überschrift (Extras).
isNeeded Ja/Nein true: mindestens eine Option muss gewählt werden, sonst REQUIRED_OPTION_MISSING.
mode Ganzzahl 1: höchstens eine Option der Gruppe (Einzelwahl), sonst Mehrfachwahl.
freeCount Ganzzahl Anzahl der Optionen der Gruppe, die ohne Aufpreis enthalten sind (die günstigsten zuerst).
deductOnDeselect Ja/Nein true: eine vorgewählte Option lässt sich abwählen (ohne …) und mindert dann den Preis.
options Liste von OnlineMenuOption? Die Optionen.

OnlineOrderItem

Eine Position einer Bestellung, wie die Kasse sie berechnet hat.

Feld Typ Bedeutung
articleId Text? Kennung des Artikels.
articleName Text? Name zum Zeitpunkt der Bestellung.
quantity Ganzzahl Menge.
unitPrice Dezimalzahl Einzelpreis einschließlich der gewählten Optionen.
totalPrice Dezimalzahl unitPrice mal quantity.
notes Text? Hinweis zur Position.
selectedOptions Liste von OnlineOrderItemOption? Die gewählten Optionen mit ihrem Aufpreis.
isAutoDeposit Ja/Nein true: die Position ist das von der Kasse ergänzte Pfand.
taxRate Dezimalzahl? Steuersatz in Prozent.

OnlineOrderItemOption

Eine gewählte Option einer Position.

Feld Typ Pflicht Bedeutung
name Text ja Name der Option genau wie in der Karte (options[].options[].name).
price Dezimalzahl nein Aufpreis in Geld. In der Anfrage wird er nicht übernommen: die Kasse bestimmt ihn aus dem Artikel.
inPrice Ja/Nein nein true: im Preis enthalten, kein Aufschlag.

OnlineOrderItemRequest

Eine Position der Bestellung.

Feld Typ Pflicht Bedeutung
articleId Text ja Kennung des Artikels aus der Karte (art_…).
quantity Ganzzahl ja Menge, 1 bis 999.
notes Text nein Hinweis zur Position (ohne Zwiebeln).
selectedOptions Liste von OnlineOrderItemOption nein Gewählte Optionen. Pflichtgruppen der Karte (isNeeded) müssen vertreten sein.

OnlineOrderResponse

Eine Online-Bestellung mit Positionen, Summen und Stand.

Feld Typ Bedeutung
id Text? Kennung der Bestellung (olord_…).
orderNumber Text? Bestellnummer zum Anzeigen (ON-20261110-001).
status Ganzzahl Stand: 1 eingegangen, 2 Zahlung läuft, 3 bezahlt, 4 angenommen, 5 in Zubereitung, 6 bereit, 7 unterwegs, 8 abgeschlossen, 9 storniert, 10 abgelehnt.
orderType Ganzzahl 0 Abholung, 1 Lieferung.
createdDate Zeitpunkt Angelegt (UTC).
changedDate Zeitpunkt Zuletzt geändert (UTC).
customerName Text? Name des Gastes.
customerEmail Text? E-Mail des Gastes.
customerPhone Text? Telefon des Gastes.
street Text? Lieferadresse: Straße.
houseNumber Text? Lieferadresse: Hausnummer.
zipCode Text? Lieferadresse: Postleitzahl.
city Text? Lieferadresse: Ort.
deliveryNotes Text? Hinweis für den Fahrer.
items Liste von OnlineOrderItem? Die Positionen.
subtotal Dezimalzahl Summe der Positionen.
deliveryFee Dezimalzahl Liefergebühr.
tipAmount Dezimalzahl Trinkgeld.
grandTotal Dezimalzahl Gesamtbetrag.
requestedTime Zeitpunkt? Wunschzeitpunkt des Gastes.
estimatedReadyTime Zeitpunkt? Voraussichtlich fertig.
confirmedAt Zeitpunkt? Angenommen am.
completedAt Zeitpunkt? Abgeschlossen am.
paymentMethod Text? Zahlart (cash, stripe, paypal).
isPaid Ja/Nein true: bezahlt.
paidAt Zeitpunkt? Bezahlt am.
amountDue Dezimalzahl Noch zu zahlender Betrag.
kitchenNotes Text? Hinweis für die Küche.
rejectionReason Text? Grund bei Ablehnung oder Stornierung.
guestSecret Text? Bestell-Geheimnis. Nur in der Antwort von create gesetzt, danach nie wieder; sofort speichern.
{
  "id": "olord_648e7d3cfa87",
  "orderNumber": "ON-20261110-001",
  "status": 1,
  "orderType": 0,
  "createdDate": "2026-11-10T17:44:05.1968974Z",
  "changedDate": "2026-11-10T17:44:05.2818688Z",
  "customerName": "Erika Muster",
  "customerEmail": "erika@example.com",
  "customerPhone": "0123 456789",
  "street": null,
  "houseNumber": null,
  "zipCode": null,
  "city": null,
  "deliveryNotes": null,
  "items": [
    {
      "articleId": "art_917c19d4a18a",
      "articleName": "Pizza Margherita",
      "quantity": 1,
      "unitPrice": 9.5,
      "totalPrice": 9.5,
      "notes": "ohne Zwiebeln",
      "selectedOptions": [],
      "isAutoDeposit": false,
      "taxRate": 7
    }
  ],
  "subtotal": 9.5,
  "deliveryFee": 0,
  "tipAmount": 0,
  "grandTotal": 9.5,
  "requestedTime": null,
  "estimatedReadyTime": null,
  "confirmedAt": null,
  "completedAt": null,
  "paymentMethod": "cash",
  "isPaid": false,
  "paidAt": null,
  "amountDue": 9.5,
  "kitchenNotes": null,
  "rejectionReason": null,
  "guestSecret": "q7Xm2LkP9vRtY4cNw8ZsB3hJdF6aGeUo"
}

OpeningHoursEntry

Öffnungszeit eines Wochentags.

Feld Typ Bedeutung
dayOfWeek Ganzzahl Wochentag, 0 Sonntag bis 6 Samstag.
isClosed Ja/Nein true: an diesem Tag geschlossen.
openTime Text? Öffnet (11:00).
closeTime Text? Schließt (22:00).
breakStart Text? Beginn der Pause, falls vorhanden.
breakEnd Text? Ende der Pause.

PartnerApiKeyCreatedResponse

Ein Partner-Schlüssel mit dem Geheimnis; nur in den Antworten von Anlegen und Erneuern.

Feld Typ Bedeutung
id Text Kennung (pak_…).
keyId Text Öffentlicher Teil des Schlüssels (nach dk1_).
name Text Bezeichnung.
scopes Liste von Text Die erlaubten Bereiche.
apiUserId Text Der technische Benutzer.
createdDate Zeitpunkt Angelegt am.
expiresAt Zeitpunkt Läuft ab am.
revokedAt Zeitpunkt? Widerrufen am.
lastUsedAt Zeitpunkt? Zuletzt benutzt am.
previousValidUntil Zeitpunkt? Nach einer Erneuerung: bis wann das alte Geheimnis noch gilt.
status Text active, expired oder revoked.
key Text Der vollständige Schlüssel (dk1_<Kennung>.<Geheimnis>). Wird nicht gespeichert und nie wieder angezeigt.

PartnerApiKeyResponse

Ein Partner-Schlüssel, ohne Geheimnis.

Feld Typ Bedeutung
id Text Kennung (pak_…).
keyId Text Öffentlicher Teil des Schlüssels (nach dk1_).
name Text Bezeichnung.
scopes Liste von Text Die erlaubten Bereiche.
apiUserId Text Der technische Benutzer.
createdDate Zeitpunkt Angelegt am.
expiresAt Zeitpunkt Läuft ab am.
revokedAt Zeitpunkt? Widerrufen am.
lastUsedAt Zeitpunkt? Zuletzt benutzt am.
previousValidUntil Zeitpunkt? Nach einer Erneuerung: bis wann das alte Geheimnis noch gilt.
status Text active, expired oder revoked.

WebhookEventResponse

Ein Ereignis, wie es der Feed ausliefert und wie es an ein Webhook-Ziel gesendet wird.

Feld Typ Bedeutung
id Text? Eindeutige Kennung (<Bestellung>.<seq>); Empfänger entdoppeln darüber.
type Text? order.created, order.status_changed, payment.received, order.cancelled oder ticket.ready.
occurredAt Text? Zeitpunkt (UTC).
tenant Text? Kennung des Betriebs.
seq Ganzzahl Laufende Nummer je Bestellung (aufsteigend, lückenlos); darüber ordnen Empfänger und erkennen Lücken.
apiVersion Text? Version der Nutzlasten.
data beliebig Die Nutzlast je type, ohne personenbezogene Angaben; Einzelheiten holt der Empfänger mit seinem Schlüssel. Beispiele: order.created: orderId, orderNumber, orderType; order.status_changed: orderId, orderNumber, status, previousStatus, isPaid.

WebhookTargetCreatedResponse

Ein Webhook-Ziel mit dem Geheimnis; nur in den Antworten von Anlegen und Erneuern.

Feld Typ Bedeutung
id Text Kennung (whz_…).
name Text Bezeichnung.
url Text https-Adresse des Empfängers.
eventTypes Liste von Text Gewünschte Ereignisarten; leer heißt alle.
enabled Ja/Nein false: das Ziel bekommt nichts; die Ereignisse bleiben im Feed.
disabledReason Text? GONE (der Empfänger antwortete 410) oder MANUAL; sonst leer.
disabledAt Zeitpunkt? Wann das Ziel abgeschaltet wurde.
pausedUntil Zeitpunkt? Wegen vieler Fehler in Folge pausiert bis.
previousSecretValidUntil Zeitpunkt? Ende der Übergangsfrist des alten Geheimnisses nach einer Erneuerung.
operatorException Ja/Nein Der Betreiber hat für dieses Ziel ein privates Netz oder einen anderen Port freigegeben.
createdDate Zeitpunkt Angelegt am (UTC).
secret Text Das Geheimnis (whsec_…) zur Prüfung der Signatur. Wird nur in dieser Antwort ausgegeben.

WebhookTargetResponse

Ein Webhook-Ziel, ohne Geheimnis.

Feld Typ Bedeutung
id Text Kennung (whz_…).
name Text Bezeichnung.
url Text https-Adresse des Empfängers.
eventTypes Liste von Text Gewünschte Ereignisarten; leer heißt alle.
enabled Ja/Nein false: das Ziel bekommt nichts; die Ereignisse bleiben im Feed.
disabledReason Text? GONE (der Empfänger antwortete 410) oder MANUAL; sonst leer.
disabledAt Zeitpunkt? Wann das Ziel abgeschaltet wurde.
pausedUntil Zeitpunkt? Wegen vieler Fehler in Folge pausiert bis.
previousSecretValidUntil Zeitpunkt? Ende der Übergangsfrist des alten Geheimnisses nach einer Erneuerung.
operatorException Ja/Nein Der Betreiber hat für dieses Ziel ein privates Netz oder einen anderen Port freigegeben.
createdDate Zeitpunkt Angelegt am (UTC).

Weiter