Schnittstelle für Kassensysteme

Karten anlegen, stempeln und abgleichen aus dem eigenen Kassensystem, der eigenen Website oder dem eigenen Buchungswerkzeug. Alles, was unsere Kasse kann, kann auch deine.

Schlüssel anfragen: Wir stellen ihn aus, weil er alle Karten eines Kontos öffnet. Eine Zeile an info@pocketstamp.ch mit dem Namen des Betriebs genügt, du bekommst ihn am selben Tag. Eine fertige Beispielbrücke in PHP und Node liegt im Projekt unter beispiele/kassenbruecke.

Für Kassensysteme, Websites und alles, was Karten anlegen, stempeln oder nachsehen soll, ohne die PocketStamp-Oberfläche zu bedienen.

https://pocketstamp.ch/api/v1

Der Aufbau: Konto, Betrieb, Standort

Drei Ebenen, und sie stehen von Anfang an fest:

Jede Adresse nennt den Betrieb ausdrücklich. Es gibt kein «der eine Betrieb», auf den sich alles bezieht.

Ausweisen

Den Schlüssel stellt PocketStamp aus (Verwaltung als Admin, Kasse › Kassensystem anbinden). Ein Betrieb bekommt ihn auf Anfrage an info@pocketstamp.ch, denn ein Schlüssel öffnet alle Karten eines Kontos. Er gehört ins Kassensystem, nie in eine Website und nie in ein öffentliches Repository.

Authorization: Bearer ps_live_a1b2c3d4e5f6_…

Ein Schlüssel gilt entweder für einen Betrieb oder für alle Betriebe seines Kontos, wahlweise fest auf einen Standort gestellt. Gespeichert wird nur ein Streuwert; beim Verlieren gibt es einen neuen, kein Nachschlagen.

Rechte, einzeln vergeben:

RechtErlaubt
lesenBetriebe, Standorte, Karten, Ereignisse, Zahlen ansehen
stempelnStempeln, Gutschein einlösen
kartenKarten anlegen, ändern, löschen
verwaltenfür spätere Schreibvorgänge an den Einstellungen
akquiseInteressenten samt Vorschaubetrieb aus einer Website anlegen. Nur für den Betreiber selbst, kein Kassensystem braucht das.

Antworten

Einzelne Sachen kommen als Objekt, Listen unter daten mit einem weiter für die nächste Seite:

{ "daten": [ … ], "weiter": 4211 }

weiter ist die Nummer, ab der es weitergeht. Beim nächsten Aufruf als ?weiter=4211 mitgeben. Steht dort null, ist die Liste zu Ende. Listen sind immer nach Nummer aufsteigend sortiert, damit ein laufender Abgleich nichts verpasst.

Fehler tragen einen Code für die Maschine und einen Satz für den Menschen:

{ "fehler": { "code": "karte_unbekannt", "text": "Diese Karte kennen wir nicht." } }

Codes: kein_zugang (401), recht_fehlt (403), betrieb_unbekannt, karte_unbekannt, standort_unbekannt, nicht_gefunden (404), methode_falsch (405), nicht_moeglich, kontingent_voll, karte_fehlt, falsche_karte (409), feld_falsch, nichts_zu_tun (422), zu_schnell, zu_viele_anfragen (429).

Grenze: 600 Aufrufe je Minute und Adresse.

Die Adressen

GET /ich

Wer der Schlüssel ist, was er darf, welche Betriebe er sieht.

curl -H "Authorization: Bearer $PS" https://pocketstamp.ch/api/v1/ich

GET /betriebe

GET /betriebe/{kuerzel}

Der einzelne Betrieb ausführlich: Programme, Standorte, Tarif, Anzahl Karten, Kontingent.

GET /betriebe/{kuerzel}/standorte

GET /betriebe/{kuerzel}/karten

Parameter: limit (bis 200, Vorgabe 50), weiter, seit (Datum oder Zeitpunkt).

POST /betriebe/{kuerzel}/karten

Legt eine Karte an, so wie es die Kasse tut. Braucht karten.

{ "art": "stempel", "quelle": "kasse", "email": "gast@example.ch" }

art wählt das Programm: stempel, prepaid, punkte, gutschein, coupon, abo. Fehlt es, gilt die Hauptkarte. Beim Gutschein gehört betrag dazu ("25" oder "25.00").

Antwort: die Karte samt code und url. Die Adresse zeigt der Kasse als QR-Code, der Gast legt die Karte damit selbst ins Wallet.

GET /karten/{code}

PATCH /karten/{code}

Ändert email oder geburtstag (MM-TT oder JJJJ-MM-TT). Leerer Wert löscht.

DELETE /karten/{code}

Stellt die Karte still. Sie verschwindet aus Listen und Zeitplan, die Daten bleiben.

POST /karten/{code}/stempel

Der Hauptvorgang. Braucht stempeln.

{ "menge": 1, "standort_id": 3, "betrag_rappen": 1250 }

Antwort:

{ "gebucht": true, "meldung": "Noch 3 bis zum Gratis-Kaffee",
  "voll": false, "karte": { … } }

Ist voll wahr, ist die Belohnung fällig. Die Sperrfrist des Betriebs gilt auch hier: zu schnell zweimal gebucht gibt zu_schnell (429).

POST /stempel

Dasselbe wie oben, aber mit dem Code im Rumpf. Für Kassen, die den Wert des Scanners einfach weiterreichen: Dort steht oft die ganze Adresse, und die passt nicht in einen Pfad.

{ "code": "https://pocketstamp.ch/karte/coffeecorner/K7QM2X", "menge": 1, "standort_id": 3 }

GET /karten?code=…

Eine Karte suchen, ohne den Code in den Pfad zu schreiben. Nimmt ebenfalls die ganze Adresse.

POST /karten/{code}/einloesen

Nur für Gutscheinkarten, mit betrag. Stempelkarten laufen über /stempel.

GET /ereignisse

Alle Buchungen, aufsteigend, mit weiter. Damit gleicht ein Kassensystem ab, was an anderen Geräten passiert ist.

{ "daten": [ { "id": 4212, "art": "stempel", "zeit": "2026-09-08T19:44:02+02:00",
               "anzahl": 1, "karte": "K7QM2X", "betrieb": "coffeecorner",
               "standort_id": 3 } ], "weiter": null }

POST /akquise

Legt einen Interessenten an, holt Logo, Farben und ein Foto von seiner Website und baut den Vorschaubetrieb, genau wie der Reiter Akquise. Braucht akquise.

{ "web": "https://cafe-elva.ch", "name": "Café Elva", "ort": "Regensdorf",
  "branche": "cafe", "belohnung": "1 Kaffee gratis", "notiz": "…" }

Nur web ist Pflicht. Fehlt name, kommt er aus dem Hostnamen. branche ist cafe, coiffeur, kosmetik oder andere. Steht die Website schon in der Liste, kommt der bestehende Eintrag zurück (200 statt 201), mit frisch geholter Marke.

Antwort: id, name, betrieb (kuerzel, belohnung, ziel), karte (der Link zur Karte), demo (der Link aus der Akquisemail), bild (die Demokarte als JPEG), verwaltung, und marke mit dem, was von der Website kam.

Dazu gehört neuer-betrieb.py im Projekt: nimmt den Schlüssel aus dem Schlüsselbund, ruft die Adresse und holt das Bild.

GET /analyse

Karten, neue Karten, Stempel und Belohnungen der letzten tage (Vorgabe 30).

Ein Kassenvorgang, von Anfang bis Ende

PS="ps_live_…"

# 1. Der Gast hat noch keine Karte: eine anlegen und den Link als QR zeigen
curl -X POST -H "Authorization: Bearer $PS" -H "Content-Type: application/json" \
     -d '{"quelle":"kasse"}' \
     https://pocketstamp.ch/api/v1/betriebe/coffeecorner/karten

# 2. Der Gast hat eine: Code aus dem Scanner, stempeln
curl -X POST -H "Authorization: Bearer $PS" -H "Content-Type: application/json" \
     -d '{"menge":1,"standort_id":3}' \
     https://pocketstamp.ch/api/v1/karten/K7QM2X/stempel

Der Scanner liefert oft die ganze Adresse statt der sechs Zeichen. Dann nicht /karten/{code}/stempel nehmen, sondern POST /stempel mit dem Wert im Rumpf: Die Schnittstelle holt den Code selbst heraus. Ein Pfad verträgt keine Adresse mit Schrägstrichen.

Was noch nicht geht