Schnittstelle für Partner (Public API)
Wozu dient das?#
Die Partner-Schnittstelle (Public API v1) ermöglicht die direkte und sichere Anbindung externer Softwaresysteme – wie eigene Webshops, Warenwirtschaftssysteme (ERP), Hotel-Management-Software (PMS) oder Buchhaltungstools – an Ihr Kassensystem. Über programmierbare Web-Schnittstellen können Partner Daten automatisch auslesen, Speisekarten aktualisieren, Lagerbestände abgleichen und Bestellungen direkt in die Kasse einsteuern.
Rollen#
- Betriebsinhaber / Administrator: Entscheidung über Drittanbieter-Anbindungen, Generierung und Konfiguration von API-Schlüsseln, Zuweisung von Berechtigungen (Scopes), Widerruf bei Sicherheitsrisiken.
- Externe Entwickler / Software-Integratoren: Technische Umsetzung der REST-API-Aufrufe, Handhabung der Authentifizierung, Einbindung des Idempotenz-Engine-Headers (
Idempotency-Key) und Einhalten der Ratenbegrenzung.
Teil 1: Leitfaden für Betriebsinhaber (Owner's Guide)#
Was ein API-Schlüssel bewirkt und erlaubt#
Ein API-Schlüssel ist ein digitaler Zugangspasswort-Ersatz für Programme. Wenn Sie einem Drittanbieter einen API-Schlüssel ausstellen, erhält dessen Software automatischen Zugriff auf die Daten Ihres Kassensystems – ohne dass sich ein Mitarbeiter manuell im Kassenmenü anmelden muss.
Die Berechtigungsebenen (Scopes) im Detail#
Sie bestimmen für jeden Schlüssel exakt, welche Aktionen der Partner ausführen darf:
| Scope (Berechtigung) | Was der Partner darf | Typischer Einsatzzweck |
|---|---|---|
orders:read | Bestellungen und Abrechnungsstatus einsehen | Buchhaltungs-Export, externe Analyse-Tools |
orders:write | Neue bezahlte Bestellungen in die Kasse einsteuern | Online-Bestellshops |
catalog:read | Speisekarte, Artikel, Kategorien und Preise auslesen | Synchronisation mit Online-Karten |
catalog:write | Produkte, Preise und Kategorien im Kassensystem anlegen/ändern | Zentrale Speisekarten-Pflege über ERP |
inventory:write | Lagerbestände und Wareneingänge anpassen | Automatischer Abgleich mit externem Lager |
analytics:read | Umsätze, Schichtberichte und Kassenkennzahlen abrufen | Dashboard-Software des Inhabers |
Gesetzliche Betreiberverantwortung nach § 146a AO (Sehr wichtig)#
Fragen-Checkliste für Inhaber VOR Vergabe eines Schreib-Schlüssels#
Stellen Sie einem Software-Partner folgende Fragen, bevor Sie ihm einen Schlüssel mit orders:write oder catalog:write erteilen:
- Nutzt die Schnittstelle Idempotenz-Header? (Verhindert doppelt erfasste Bestellungen bei Netzwerkausfällen).
- Übermittelt das System korrekte Steuersätze (7 % vs. 19 %)? (Falsche Steuersätze im Online-Shop führen zu fehlerhafter TSE-Signierung).
- Wie werden Stornierungen von Online-Bestellungen gehandhabt? (Stornierungen müssen über ordnungsgemäße Kassen-Stornowege erfasst werden).
- Wer haftet bei technischen Übertragungsfehlern? (Klären Sie vertraglich, wer Fehlbuchungen korrigiert).
Voraussetzungen für Inhaber#
- Ein Administrator-Zugang für die Verwaltungs-Schnittstelle.
- Ein konfigurierter Mandanten-Standort.
Bildschirm für Bildschirm (Inhaber-Ansicht)#
Für API-Schlüssel gibt es keinen Bildschirm. Im Backoffice finden Sie weder einen Menüpunkt noch einen Dialog dafür. Schlüssel werden ausschließlich über die Verwaltungs-Schnittstelle (/api/v1/admin) angelegt, gesperrt und gelöscht; wer sie bedient, braucht ein Administrator-Konto und einen Integrator, der die Aufrufe schickt.
- Schlüssel anlegen:
POST /api/v1/admin/api-keysmit Name, Scopes (orders:read,orders:write,catalog:read,catalog:write,inventory:write,analytics:read) und Ratenbegrenzung (Standard:60Anfragen pro Minute). Die Antwort enthält den Klartext-Schlüssel (plaintext_key) genau einmal; er lässt sich danach nicht mehr abrufen. - Schlüssel auflisten:
GET /api/v1/admin/api-keysliefert Namen, Schlüssel-Präfix (z. B.pk_live_a1b2c3d4), Scopes, Status (is_active), Erstellungsdatum und die letzte Nutzung. - Schlüssel sperren:
POST /api/v1/admin/api-keys/{key_id}/revoke. - Schlüssel löschen:
DELETE /api/v1/admin/api-keys/{key_id}.
Schritt für Schritt (Inhaber-Abläufe)#
Pfad A: Lese-Schlüssel für Steuerberater oder Analyse-Tool erstellen#
- Beauftragen Sie Ihren Integrator (oder rufen Sie die Schnittstelle selbst mit Administrator-Zugang auf),
POST /api/v1/admin/api-keysaufzurufen. - Name z. B. "DATEV Buchhaltungs-Export".
- Scopes ausschließlich die Lese-Scopes
orders:readundanalytics:read. - Übermitteln Sie den einmalig angezeigten Schlüssel an Ihren Berater.
Pfad B: Schreib-Schlüssel für Lieferdienst oder Webshop erstellen#
- Rufen Sie
POST /api/v1/admin/api-keysauf, Name z. B. "Webshop-Anbindung". - Scopes
orders:read,orders:writeundcatalog:read. - Prüfen Sie, ob der Partner den Idempotenz-Header unterstützt.
- Kopieren Sie den einmalig angezeigten Klartext-Schlüssel (
pk_live_...) und tragen Sie ihn im Konfigurationsportal des Partners ein.
Pfad C: Kompromittierten oder veralteten Schlüssel sofort widerrufen (Revoke)#
- Wenn die Zusammenarbeit mit einem Partner endet oder der Schlüssel versehentlich öffentlich wurde, suchen Sie ihn über
GET /api/v1/admin/api-keys. - Sperren Sie ihn mit
POST /api/v1/admin/api-keys/{key_id}/revoke. - Das Kassensystem setzt den Schlüssel sofort auf inaktiv (
is_active: false,revoked_at: timestamp). Jede nachfolgende Anfrage des Partners wird augenblicklich vom Server blockiert (401 Unauthorized).
Teil 2: Technische Referenz für Entwickler & Integratoren#
Authentifizierung & Base-URL#
Alle Aufrufe der Public API v1 müssen über HTTPS an die Basis-URL gerichtet werden: HTTPS https://<Ihre-Domain>/api/public/v1
Der API-Schlüssel wird im HTTP-Header als Bearer-Token übermittelt:
Authorization: Bearer pk_live_a1b2c3d4_e5f6g7h8i9j0k1l2m3n4o5p6
Content-Type: application/json
Ratenbegrenzung (Rate Limiting)#
Jeder API-Schlüssel unterliegt einer dynamischen Ratenbegrenzung (Standard: 60 Anfragen pro Minute). Bei Überschreitung antwortet das Kassensystem mit HTTP 429 Too Many Requests und sendet einen Retry-After-Header:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{
"detail": "Rate limit exceeded (max 60 requests/min)"
}
Atomare Idempotenz-Engine (Idempotency-Key)#
Um bei Netzwerkunterbrechungen oder Timeouts doppelt erfasste Bestellungen oder doppelte TSE-Signierungen strikt auszuschließen, unterstützen alle schreibenden Endpunkte (POST, PUT) den Idempotency-Key-Header.
Client-Anforderung: Senden Sie bei jedem schreibenden Aufruf eine eindeutige UUIDv4 im Header:
Idempotency-Key: e4a0b3c2-1234-5678-90ab-cdef12345678
Verhalten des Kassensystems bei Idempotenz:#
- Erfolgreicher Replay (Wiederholung): Wird eine Anfrage mit einem bereits erfolgreich verarbeiteten
Idempotency-Keyerneut gesendet, antwortet das Kassensystem mit dem gecachten Original-Statuscode und Payload. Die Antwort enthält den HeaderX-Idempotent-Replay: true. Es wird keine zweite Bestellung und keine zweite TSE-Signatur erzeugt. - Paralleler Aufruf (Konflikt 409): Trifft eine zweite Anfrage mit identischem
Idempotency-Keyein, während die Erstverarbeitung noch läuft, antwortet der Server mitHTTP 409 Conflict: ``http HTTP/1.1 409 Conflict Retry-After: 2 Content-Type: application/json { "detail": "A request with this Idempotency-Key is currently in progress. Please retry after completion." }`` - Lease-Zeitfenster & Vorhaltezeit: Das In-Progress-Lease-Fenster beträgt 180 Sekunden (ausgelegt für maximale TSE-Remote-Signatur-Timeouts). Idempotenz-Records werden im System für 24 Stunden aufbewahrt.
Verifizierte Code-Beispiele (Request & Response)#
Beispiel 1: Bezahlte Online-Bestellung einsteuern (POST /api/public/v1/orders)#
Request:
POST /api/public/v1/orders
Authorization: Bearer pk_live_a1b2c3d4_e5f6g7h8i9j0k1l2m3n4o5p6
Idempotency-Key: 9b1deb4d-3b7d-4149-9cd6-89a35d7e63ee
Content-Type: application/json
{
"location_id": "main",
"items": [
{
"product_id": "prod_1001",
"quantity": 2,
"note": "Ohne Zwiebeln"
}
],
"status": "PAID",
"payment_method": "CARD"
}
Response (HTTP 201 Created):
{
"id": "ord_8849201",
"tenant_id": "demo",
"location_id": "main",
"order_number": "BON-1004",
"status": "CLOSED",
"total_cents": 2500,
"tax_cents": 399,
"currency": "EUR",
"created_at": "2026-08-07T01:30:00Z",
"closed_at": "2026-08-07T01:30:01Z",
"items": [
{
"id": "item_1",
"product_id": "prod_1001",
"product_name": "Cheeseburger Deluxe",
"quantity": 2,
"unit_price_cents": 1250,
"total_price_cents": 2500,
"note": "Ohne Zwiebeln"
}
]
}
Beispiel 2: Neuen Speisekarten-Artikel anlegen (POST /api/public/v1/catalog/products)#
Request:
POST /api/public/v1/catalog/products
Authorization: Bearer pk_live_a1b2c3d4_e5f6g7h8i9j0k1l2m3n4o5p6
Idempotency-Key: prod-create-uuid-777
Content-Type: application/json
{
"name": "Hausgemachte Limonade",
"description": "Erfrischende Zitronen-Minz-Limonade 0,5l",
"price_cents": 480,
"tax_rate": 0.19,
"category_id": "cat_getraenke",
"is_available": true
}
Response (HTTP 201 Created):
{
"id": "prod_5501",
"tenant_id": "demo",
"category_id": "cat_getraenke",
"name": "Hausgemachte Limonade",
"description": "Erfrischende Zitronen-Minz-Limonade 0,5l",
"price_cents": 480,
"tax_rate_percent": 19.0,
"is_available": true
}
Beispiel 3: Lagerbestand anpassen (POST /api/public/v1/inventory/adjustments)#
Request:
POST /api/public/v1/inventory/adjustments
Authorization: Bearer pk_live_a1b2c3d4_e5f6g7h8i9j0k1l2m3n4o5p6
Idempotency-Key: inv-adj-uuid-888
Content-Type: application/json
{
"location_id": "main",
"product_id": "prod_5501",
"delta": 50,
"reason": "restock",
"note": "Wareneingang Kiste #402"
}
Response (HTTP 200 OK):
{
"tenant_id": "demo",
"location_id": "main",
"product_id": "prod_5501",
"quantity": 50,
"track_stock": true
}
Webhook erneut senden#
Ist die Zustellung einer Webhook-Nachricht an Ihr System fehlgeschlagen oder endgültig gescheitert (Dead Letter), können Administratoren und Manager sie erneut senden. Dafür gibt es keine Bedienoberfläche, der Aufruf läuft nur über die Schnittstelle: POST /api/v1/admin/webhooks/deliveries/{delivery_id}/replay mit der Kennung der Zustellung. Antwort ist die Zustellung mit ihrem neuen Stand. Beheben Sie vorher die Ursache auf Ihrer Seite, sonst scheitert auch der zweite Versuch.
Was passiert im Hintergrund#
Wenn ein externer Aufruf an die Schnittstelle gesendet wird, prüft das Middleware-Modul auth.py den Authorization-Header. Der empfangene Schlüssel wird mit SHA-256 gehasht und in der Datenbank mit der Tabelle ApiKey verglichen. Stimmen Hash und Mandanten-ID überein und ist der Schlüssel aktiv, wird die Anfrage an das Servicemodul weitergeleitet.
Bestellungen mit dem Status PAID triggern automatisch das Fiskalmodul, lösen Event-Outbox-Nachrichten für Küchenmonitore (KDS) aus und senden Webhooks an angemeldete Empfänger.
Häufige Probleme (Original-Fehlermeldungen aus dem Backend)#
1. Meldung: Missing Authorization Bearer token#
- Ursache: Die Anfrage enthält keinen
Authorization-Header oder das WortBearerwurde vergessen. - Lösung: Fügen Sie den Header
Authorization: Bearer <Ihr_Schlüssel>zur HTTP-Anfrage hinzu.
2. Meldung: Invalid API key format#
- Ursache: Der übergebene Schlüssel entspricht nicht dem geforderten Syntax-Format (z. B. fehlerhaftes Präfix oder ungültige Zeichen).
- Lösung: Überprüfen Sie den Schlüssel auf Vollständigkeit und kopieren Sie ihn erneut vom Inhaber (der Klartext wird nur einmal angezeigt; sonst neu anlegen).
3. Meldung: Invalid or revoked API key#
- Ursache: Der verwendete API-Schlüssel existiert nicht im System oder wurde vom Inhaber widerrufen.
- Lösung: Prüfen Sie über
GET /api/v1/admin/api-keysden Status des Schlüssels oder generieren Sie einen neuen Schlüssel.
4. Meldung: API key has been revoked or deactivated#
- Ursache: Der API-Schlüssel wurde manuell gesperrt.
- Lösung: Der Inhaber muss über die Verwaltungs-Schnittstelle einen neuen Schlüssel erstellen.
5. Meldung: API key has expired#
- Ursache: Das eingestellte Ablaufdatum (
expires_at) des API-Schlüssels wurde überschritten. - Lösung: Erstellen Sie über die Verwaltungs-Schnittstelle einen neuen API-Schlüssel ohne Ablaufdatum oder mit verlängerter Laufzeit.
6. Meldung: API key missing required scope: {required_scope}#
- Ursache: Der verwendete Schlüssel besitzt nicht die erforderliche Berechtigung für diesen Endpunkt (z. B. Versuch einer Bestellung ohne den Scope
orders:write). - Lösung: Legen Sie den Schlüssel mit den benötigten Scopes neu an (Scopes lassen sich nicht nachträglich ändern).
7. Meldung: Rate limit exceeded (max {rate_limit_per_minute} requests/min)#
- Ursache: Das System des Partners hat mehr Anfragen pro Minute gesendet, als für diesen Schlüssel erlaubt sind (Standard: 60).
- Lösung: Der Partner muss Anfragen drosseln, Zusammenfassungen (Batching) nutzen oder Antworten lokal zwischenspeichern.
8. Meldung: A request with this Idempotency-Key is currently in progress. Please retry after completion.#
- Ursache: Zwei parallele Anfragen mit identischem
Idempotency-Keywurden zeitgleich an den Server gesendet (HTTP 409 Conflict). - Lösung: Warten Sie die vorgegebene
Retry-After-Zeit ab, bevor Sie die Anfrage erneut senden.
9. Meldung: Idempotency key reservation lease lost due to timeout#
- Ursache: Die Verarbeitung der ersten Anfrage hat das maximale Lease-Fenster von 180 Sekunden überschritten (z. B. durch TSE-Verzögerung).
- Lösung: Senden Sie die Anfrage mit einem neuen
Idempotency-Keyerneut.
10. Meldung: API key not found#
- Ursache: Die in der Anfrage enthaltene Schlüssel-ID existiert nicht in der Datenbank des Mandanten.
- Lösung: Stellen Sie sicher, dass Sie den Schlüssel für den richtigen Mandanten (Tenant) verwenden.
Grenzen#
- Keine fiskalische Umgehung: Es ist technisch unmöglich, über die API bezahlte Bestellungen einzusteuern, ohne dass diese von der TSE signiert werden.
- Mandanten-Isolation: Ein API-Schlüssel kann niemals standortübergreifend auf Daten anderer Gastronomiebetriebe zugreifen.
- Keine Wiederherstellung von Klartext-Schlüsseln: Da das System nur SHA-256-Hashes speichert, kann ein verlorener Schlüssel nicht wieder ausgelesen werden.
Dieses Kapitel gehört zum Merkuron-Handbuch für Inhaber und Servicepersonal.