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:
- Konto — wem alles gehört. Eine Gruppe mit einer Bäckerei und einem Café hat ein Konto und zwei Betriebe.
- Betrieb — eine Marke mit einer Kundenkarte, angesprochen über sein Kürzel (
coffeecorner). Ein Betrieb kann mehrere Karten führen (Stempelkarte, Guthabenkarte, Gutschein); die heissen Programme und haben je ein eigenes Kürzel. - Standort — die einzelne Filiale. Jede Buchung kann einen Standort tragen, damit die Auswertung weiss, wo gestempelt wurde.
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:
| Recht | Erlaubt |
|---|---|
lesen | Betriebe, Standorte, Karten, Ereignisse, Zahlen ansehen |
stempeln | Stempeln, Gutschein einlösen |
karten | Karten anlegen, ändern, löschen |
verwalten | für spätere Schreibvorgänge an den Einstellungen |
akquise | Interessenten 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 }
menge— wie viele Stempel, 1 bis 10.standort_id— welche Filiale. Entfällt, wenn der Schlüssel fest auf einen Standort steht.betrag_rappen— nur bei der Punktekarte, dort Pflicht.
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
- Schreiben an den Einstellungen eines Betriebs (
verwaltenist vergeben, aber noch ohne Adressen). - Webhooks. Bis dahin fragt man
/ereignisseab, das ist für eine Kasse günstig genug. - Standorte anlegen und ändern. Das geht in der Verwaltung.