Zur Startseite Live-Demo

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 darfTypischer Einsatzzweck
orders:readBestellungen und Abrechnungsstatus einsehenBuchhaltungs-Export, externe Analyse-Tools
orders:writeNeue bezahlte Bestellungen in die Kasse einsteuernOnline-Bestellshops
catalog:readSpeisekarte, Artikel, Kategorien und Preise auslesenSynchronisation mit Online-Karten
catalog:writeProdukte, Preise und Kategorien im Kassensystem anlegen/ändernZentrale Speisekarten-Pflege über ERP
inventory:writeLagerbestände und Wareneingänge anpassenAutomatischer Abgleich mit externem Lager
analytics:readUmsätze, Schichtberichte und Kassenkennzahlen abrufenDashboard-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:

  1. Nutzt die Schnittstelle Idempotenz-Header? (Verhindert doppelt erfasste Bestellungen bei Netzwerkausfällen).
  2. Übermittelt das System korrekte Steuersätze (7 % vs. 19 %)? (Falsche Steuersätze im Online-Shop führen zu fehlerhafter TSE-Signierung).
  3. Wie werden Stornierungen von Online-Bestellungen gehandhabt? (Stornierungen müssen über ordnungsgemäße Kassen-Stornowege erfasst werden).
  4. 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-keys mit Name, Scopes (orders:read, orders:write, catalog:read, catalog:write, inventory:write, analytics:read) und Ratenbegrenzung (Standard: 60 Anfragen 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-keys liefert 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#

  1. Beauftragen Sie Ihren Integrator (oder rufen Sie die Schnittstelle selbst mit Administrator-Zugang auf), POST /api/v1/admin/api-keys aufzurufen.
  2. Name z. B. "DATEV Buchhaltungs-Export".
  3. Scopes ausschließlich die Lese-Scopes orders:read und analytics:read.
  4. Übermitteln Sie den einmalig angezeigten Schlüssel an Ihren Berater.

Pfad B: Schreib-Schlüssel für Lieferdienst oder Webshop erstellen#

  1. Rufen Sie POST /api/v1/admin/api-keys auf, Name z. B. "Webshop-Anbindung".
  2. Scopes orders:read, orders:write und catalog:read.
  3. Prüfen Sie, ob der Partner den Idempotenz-Header unterstützt.
  4. 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)#

  1. Wenn die Zusammenarbeit mit einem Partner endet oder der Schlüssel versehentlich öffentlich wurde, suchen Sie ihn über GET /api/v1/admin/api-keys.
  2. Sperren Sie ihn mit POST /api/v1/admin/api-keys/{key_id}/revoke.
  3. 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:#

  1. Erfolgreicher Replay (Wiederholung): Wird eine Anfrage mit einem bereits erfolgreich verarbeiteten Idempotency-Key erneut gesendet, antwortet das Kassensystem mit dem gecachten Original-Statuscode und Payload. Die Antwort enthält den Header X-Idempotent-Replay: true. Es wird keine zweite Bestellung und keine zweite TSE-Signatur erzeugt.
  2. Paralleler Aufruf (Konflikt 409): Trifft eine zweite Anfrage mit identischem Idempotency-Key ein, während die Erstverarbeitung noch läuft, antwortet der Server mit HTTP 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." } ``
  3. 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 Wort Bearer wurde 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-keys den 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-Key wurden 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-Key erneut.

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.