API

Für Kassensysteme, Warenwirtschaft und alles, was Daten liefert oder abholt.

Basisadresse https://api.bum2.fun/api/v1

Alle Antworten sind JSON in UTF-8. Es gibt keinen CORS-Kopf: das hier ist eine Maschinenschnittstelle, und ein Schlüssel, den ein Browser mitschickt, steht im Quelltext einer Webseite.

Anmeldung

Jeder Aufruf braucht einen API-Schlüssel im Kopf Authorization. Erzeugt wird er im Portal unter Einstellungen → API-Schlüssel.

curl https://api.bum2.fun/api/v1/artikel \
  -H 'Authorization: Bearer ak_…'

Der Schlüssel bestimmt den Tenant.

Er steht in keinem Aufrufparameter und lässt sich nicht wählen. Wer einen Schlüssel hat, sieht die Daten des Tenants, zu dem er gehört — und keine anderen.

Der Schlüssel wird einmal angezeigt und danach nie wieder; gespeichert ist nur sein SHA-256-Hash. Geht er verloren, legt man einen neuen an und sperrt den alten. Ein Schlüssel kann lesen oder lesen und schreiben — eine Auswertung, die Zahlen abholt, braucht das Schreibrecht nicht.

Nicht als Abfrageparameter mitgeben: der stünde in jedem Zugriffsprotokoll des Webservers, in jedem Proxy und in der Browserhistorie.

Objekte

Sechs Objekte, in dieser Reihenfolge aufeinander aufbauend. Preise, Bestände und Verkäufe verweisen auf Artikel und Standorte — die müssen also zuerst da sein.

PfadObjektZweck
/api/v1/store Standorte Betriebe, Filialen, Restaurants. Muss VOR Preisen und Beständen hochgeladen werden — die verweisen darauf.
/api/v1/artikel Artikel Der Artikelstamm. Preise und Bestände kommen in eigenen Dateien — ein Artikel kostet je Standort verschieden.
/api/v1/preis Preise Welcher Artikel kostet an welchem Standort wie viel. Ein Artikel ohne Zeile hier ist an diesem Standort nicht im Angebot.
/api/v1/bestand Bestände Momentaufnahme je Artikel und Standort. Keine Historie — ein neuer Upload ersetzt den Stand.
/api/v1/kunde Kunden Stammkunden. Für die Gastronomie meist zweitrangig, für den Handel die Grundlage jeder Kundenauswertung.
/api/v1/verkauf Verkäufe Bons und ihre Positionen. EINE ZEILE JE POSITION — ein Bon mit drei Artikeln steht dreimal darin, jedes Mal mit derselben Bonnummer. Ohne diese Daten gibt es keine Auswertung, keine ABC-Analyse und keine Prognose.

Lesen

GET /api/v1/artikel?limit=100&offset=0
GET /api/v1/artikel/100-001

limit geht bis 1000 (Vorgabe 100). Einzeln lesbar sind nur Objekte mit eigener Nummer: store, artikel, kunde, verkauf. Preis und Bestand hängen an Artikel und Standort und haben keine eigene — sie kommen über den Artikel.

Schreiben

POST /api/v1/verkauf
Content-Type: application/json

{"daten": [
  {"bonnummer": "2026-0001", "zeitpunkt": "2026-08-15 19:42",
   "storenummer": "RIVA", "artikelnummer": "100-001",
   "menge": 2, "einzelpreis": 33.00}
]}

Alles oder nichts.

Ist eine Zeile fehlerhaft, wird keine geschrieben, und die Antwort nennt die betroffenen Zeilen einzeln. Der Gegenentwurf — gute Zeilen übernehmen, schlechte melden — hinterlässt einen halb übertragenen Bon, und niemand weiss hinterher, welche Hälfte.

Kein Vollabgleich

Über die API wird ergänzt und aktualisiert, nie stillgelegt. Ein Vollabgleich, der fehlende Sätze abschaltet, bleibt der Upload-Seite im Portal vorbehalten.

Der Grund ist die Betriebsart: eine Kasse schickt Teilmengen — den Umsatz einer Schicht, die geänderten Preise von heute. Was darin fehlt, ist nicht abgeschafft. Würde die API stilllegen, was sie nicht sieht, legte der erste Aufruf einer Kasse das halbe Sortiment still, und die Antwort wäre ein freundliches 200.

Felder

Aus derselben Beschreibung wie die Excel-Vorlagen. Mit Pflicht gekennzeichnete Felder müssen in jedem Datensatz stehen.

Standorte /api/v1/store

FeldTyp BeispielHinweis
nummer Pflicht text RIVA Eindeutiges Kürzel, z. B. RIVA. Unveränderlich — es verbindet alle anderen Dateien mit diesem Standort.
name Pflicht text Riva Wie der Standort im Haus heisst.
typ auswahl
filiale · restaurant · hotel · online · lager · sonstige
restaurant Leer = filiale.
strasse text Ketschauerhofstr. 1
plz text 67146 Als TEXT eintragen — führende Nullen gehen sonst verloren.
ort text Deidesheim
land text DE Zwei Buchstaben nach ISO 3166, z. B. DE, AT, CH.
waehrung text EUR Leer = EUR. Gilt für alle Preise dieses Standorts.

Artikel /api/v1/artikel

FeldTyp BeispielHinweis
nummer Pflicht text 100-001 Die Artikelnummer aus deinem System. Sie verbindet Preise, Bestände und Verkäufe mit diesem Artikel.
bezeichnung Pflicht text 2023 Riesling „Herrgottsacker"
warengruppe text Wein Flasche weiß Frei wählbar, aber EINHEITLICH schreiben — „Wein weiss" und „Weisswein" sind zwei Gruppen.
hersteller text Reichsrat von Buhl
ean text 4006381333931 Als TEXT eintragen, sonst macht Excel 4,00612E+12 daraus.
basiseinheit text FL Leer = ST. Sonst FL, KG, L, PORTION …
gewicht zahl 1,25 In KILOGRAMM, immer. 750 g sind 0,75 — eine Spalte für die Einheit gibt es bewusst nicht, sonst stehen Gramm und Kilo in derselben Summe. Für Versandkosten und Liefergewicht.
jahrgang zahl 2023 Nur bei Wein. Derselbe Wein ist 2019 ein anderer Artikel als 2020.
status auswahl
aktiv · auslaufend · gesperrt
aktiv Leer = aktiv.
beschreibung text

Preise /api/v1/preis

FeldTyp BeispielHinweis
artikelnummer Pflicht text 100-001 Muss in der Artikeldatei vorkommen.
storenummer Pflicht text RIVA Muss in der Standortdatei vorkommen.
preis Pflicht zahl 33,00 Bruttoverkaufspreis. Komma oder Punkt, beides geht.
gueltig_ab datum 01.03.2026 Leer = ab heute. Ein früherer Preis wird zu diesem Zeitpunkt beendet, nicht überschrieben.

Bestände /api/v1/bestand

FeldTyp BeispielHinweis
artikelnummer Pflicht text 100-001
storenummer Pflicht text RIVA
menge Pflicht zahl 12 Darf 0 sein. Leer ist etwas anderes als 0 und wird abgewiesen.
einheit text FL Leer = die Basiseinheit des Artikels.
mindestmenge zahl 6 Ab wann nachbestellt wird. Ohne diesen Wert kann der Assistent nicht sagen, was knapp wird.
stand datumzeit 14.08.2026 18:00 Zeitpunkt der Zählung. Leer = jetzt.

Kunden /api/v1/kunde

FeldTyp BeispielHinweis
nummer Pflicht text K-1001
name Pflicht text Bessie Wilhelm
email text [email protected]
strasse text Hauptstr. 3
plz text 67146 Als TEXT — sonst fehlen führende Nullen.
ort text Deidesheim
land text DE
angelegt_am datum 11.09.2023 Seit wann Kunde. Ohne dieses Datum lässt sich Neukundengewinnung nicht auswerten.

Verkäufe /api/v1/verkauf

FeldTyp BeispielHinweis
bonnummer Pflicht text 2026-0001 Gleiche Nummer = gleicher Bon.
zeitpunkt Pflicht datumzeit 14.08.2026 19:42 Datum UND Uhrzeit. Nur ein Datum macht jede Tageszeitauswertung unmöglich.
storenummer Pflicht text RIVA
artikelnummer Pflicht text 100-001
menge Pflicht zahl 2
einzelpreis Pflicht zahl 33,00 Der TATSÄCHLICH bezahlte Preis, nicht der Listenpreis. Sonst ändert sich der Umsatz der Vergangenheit bei jeder Preispflege.
rabatt zahl 0 Leer = 0.
kundennummer text Leer = anonym. Im Restaurant der Normalfall.
storniert ja_nein nein ja/nein. Leer = nein. Stornierte Bons zählen nicht zum Umsatz, bleiben aber sichtbar.

Fehler

Nach RFC 9457 (Problem Details) — ein festes Format, damit ein Aufrufer nicht raten muss: title für Menschen, type zum Verzweigen im Code, errors für die Zeilen, die nicht durchkamen.

{
  "type": "https://api.bum2.fun/fehler/anmeldung",
  "title": "Der API-Schlüssel wird nicht angenommen.",
  "status": 401
}
CodetypeBedeutung
401…/anmeldung Kein oder ungültiger Schlüssel. Ein gesperrter Schlüssel bekommt dieselbe Meldung wie ein erfundener — ein Unterschied verriete, dass es ihn gibt.
403…/recht Schlüssel darf nur lesen, der Aufruf will schreiben.
404…/objekt Unbekanntes Objekt. moeglich listet die gültigen.
404…/nichtgefunden Diese Nummer gibt es in diesem Tenant nicht.
400…/daten Zeilen fehlerhaft. errors nennt sie mit Zeilennummer.
503…/datenbank Keine Datenbankverbindung. Später erneut versuchen.

Postman

Eine Sammlung mit allen Aufrufen, erzeugt aus derselben Beschreibung wie diese Seite — sie kann also nicht veralten, solange die Schnittstelle sich nicht ändert.

Sammlung herunterladen

In Postman: Import → Datei wählen. Danach in der Sammlung unter Variables den Wert schluessel auf den eigenen API-Schlüssel setzen; er gilt dann für alle Aufrufe.

Grenzen

Grösse eines Aufrufs16 MB
Zeilen je POST keine feste Grenze — praktisch begrenzt das Zeitlimit
Antwortzeit Cloudflare bricht nach 100 Sekunden ab. Bei sehr vielen Zeilen in Teilmengen schicken.
Aufrufe je Minutederzeit nicht begrenzt

Objektkatalog

Die Schnittstelle beschreibt sich selbst — hinter der Anmeldung:

curl https://api.bum2.fun/api/v1/ \
  -H 'Authorization: Bearer ak_…'

Der Katalog steht bewusst hinter der Anmeldung. Er stand einmal davor, und GET /api/v1/ antwortete deshalb mit 200 auch auf einen erfundenen Schlüssel — damit taugte der Endpunkt nicht mehr zum Prüfen der Einrichtung.