APIs

7 Seiten

Stand: 6. November 2026

Umsatz API

Die Umsatz-API liefert alle umsatzrelevanten Transaktionen eines Zeitraums — samt Positionen, Zahlungen und Steueraufteilung — in einer einzigen Abfrage. Ideal für Buchhaltungsexporte, Umsatzabstimmung, BI-Warehouses oder Ad-hoc-Reports.

Tipp: Umsatz API oder SQL Views?
Die Umsatz-API und die SQL Views liefern
dieselben Daten in unterschiedlicher Form für unterschiedliche
Laufzeiten:

- Umsatz API — über den öffentlichen Cloud-Endpunkt
(my.apro.at). Pro Beleg ein hierarchisches JSON-Objekt mit
Positionen, Zahlungen, Steuersätzen, Bediener und Tisch bereits
verschachtelt. Ideal für Off-Premise-Systeme, SaaS-Integrationen,
Mobile Clients — überall, wo ein fertiger Objektbaum gewünscht ist.
- SQL Views — lesende Views direkt auf der
On-Premise-Kassendatenbank. Flach, relational, mit jedem
SQL-Server-fähigen Tool abfragbar. Ideal für On-Premise-
Integrationen, ETL-Pipelines und **bestehende BI-Workflows
(Power BI, Tableau, Qlik, Metabase)**.

Cloud-API für Realtime-System-zu-System-Integration, SQL-Views für
Bulk-Analytics und Dashboards.
  • Host: my.apro.at
  • Auth: API-Key im x-api-key
  • Content type: application/json

Endpunkte

Variante

Endpunkt

Standort aus

Pfad + Key

POST /api/v1/locations/{location}/reports/receipts

{location} muss mit dem Standort des Keys übereinstimmen.

Nur Key

POST /api/v1/reports/receipts

dem API-Key selbst.

Beide Varianten erfordern einen standortgebundenen API-Key. Der Key selbst bestimmt, welche Standortdaten geliefert werden — einen mandantenweiten Endpunkt gibt es nicht. Wähle, was im Client-Code klarer ist — beide liefern dieselben Daten und benötigen denselben Key.

Request-Body

Feld

Typ

Pflicht

Beschreibung

after

ISO 8601 Timestamp

✅

Nur Belege nach diesem Zeitpunkt.

before

ISO 8601 Timestamp

✅

Nur Belege vor diesem Zeitpunkt.

limit

integer

—

Anzahl zurückgelieferter Belege begrenzen.

afterTransactionID

GUID

—

Cursor — Transaktionen nach dieser ID liefern. Für Paging zusammen mit limit nutzen.

Beispielanfrage

POST /api/v1/reports/receipts

{
  "after":  "2026-01-01T00:00:00Z",
  "before": "2026-02-01T00:00:00Z",
  "limit":  100,
  "afterTransactionID": "f03e11de-6330-4a03-a8a8-b6e5fd4e32a5"
}

Download: revenue-request.json

Antwort

Ein Array von Belegen. Jedes Element ist eine Transaktion mit Positionen, Zahlungen und Metadaten.

Top-Level-Felder

Feld

Typ

Beschreibung

transactionId

GUID

Eindeutige Transaktions-ID.

transactionType

string

Siehe Transaktionstypen.

receiptType

string

Beleg, Ausgangsrechnung oder Lieferschein.

totalAmount

decimal

Bruttogesamtbetrag.

tip

decimal

Im totalAmount enthaltenes Trinkgeld.

discount

decimal

Abgezogener Rabatt.

dateTime

ISO 8601

Zeitpunkt der Transaktion.

business

Business

Unternehmen / Mandant.

location

Location

Der APRO-Standort, für den abgefragt wurde.

staff

Staff

Bediener / Kellner.

table

Table

Tisch oder Bestellziel (z. B. Bar 1, Zimmer 301).

area

Area

Bereich / Zone des Tisches (z. B. Garten, Bar).

customer

Customer · nullable

Rechnungsempfänger. Auf diesem Endpunkt immer null — siehe unten.

paymentGroup

Payment group

Gruppierung, in die die Zahlungsarten zusammenlaufen.

payments

[Payment]

Zahlungen zum Beleg.

items

[Item]

Belegpositionen.

guestCount

integer

Gästeanzahl, summiert über die Positionen des Belegs. 0, wenn nicht erfasst.

Item

Feld

Typ

Beschreibung

id

string

Eindeutige Artikel-ID.

number

integer

Artikelnummer (in APRO). 0, wenn nicht parsebar.

name

string

Artikelname.

uniqueId

string · nullable

Stabiler Identifier je Position. Zum Deduplizieren bzw. Abgleichen über wiederholte Abfragen hinweg. null bei älteren Query-Konfigurationen.

externalId

string · nullable

Referenz aus einem Fremdsystem. null bei älteren Query-Konfigurationen.

bookDate

ISO 8601

Buchungszeitpunkt.

grossAmount

decimal

Bruttopreis.

netAmount

decimal

Nettopreis.

purchasePrice

decimal

Einkaufspreis der Position, für Deckungsbeitrags-Auswertungen. 0 bei älteren Query-Konfigurationen.

quantity

decimal

Stückzahl.

course

integer · nullable

Gang bei mehrgängigem Service. null, wenn nicht zugeordnet.

tax

Tax

Angewendeter Steuersatz.

category

Category

Sparte.

sideDishes

[Side dish] · nullable

Unterartikel / Beilagen zu dieser Position. null, wenn keine vorhanden sind.

Side dish

Unterartikel sind die Beilagen und Modifikatoren, die unter einer Position gebucht werden — die Pommes zum Schnitzel, das ohne Zwiebel zum Burger. Sie werden verschachtelt in der übergeordneten Position zurückgegeben, nie als eigene Positionen.

Ein Unterartikel nutzt dieselbe Item-Struktur, befüllt aber nur die folgenden Felder. Alle übrigen — bookDate, externalId, purchasePrice, course, tax, category, sideDishes — sind null bzw. ihr Default-Wert; Unterartikel verschachteln sich nicht rekursiv.

Feld

Typ

Beschreibung

id

string

Eindeutige Unterartikel-ID.

number

integer

Artikelnummer des Unterartikels. 0, wenn nicht parsebar.

name

string

Bezeichnung des Unterartikels.

uniqueId

string

Stabiler Identifier je Unterartikel. Zugleich der Schlüssel, über den dedupliziert wird.

grossAmount

decimal

Bruttopreis des Unterartikels.

netAmount

decimal

Nettopreis des Unterartikels.

quantity

decimal

Stückzahl des Unterartikels.

Achtung: Unterartikel nicht zu den Summen addieren
Ein Unterartikel mit Aufpreis ist **bereits im grossAmount /
netAmount der übergeordneten Position** und im totalAmount des
Belegs enthalten. Wer sideDishes[].grossAmount zusätzlich aufaddiert,
zählt Umsatz doppelt. Die Beträge sind eine Aufschlüsselung für die
Anzeige, keine eigenständigen Positionen.

sideDishes ist null — nicht [] — wenn eine Position keine Unterartikel hat, ebenso bei älteren Query-Konfigurationen aus der Zeit vor diesem Feature. Daher auf null-oder-leer prüfen, statt ein Array vorauszusetzen.

Tax

Feld

Typ

Beschreibung

id

string

Eindeutige ID des Steuersatzes.

value

decimal

Höhe, z. B. 20.0 für 20 %.

name

string

Bezeichnung, z. B. Mwst 20 %.

Category

Feld

Typ

Beschreibung

id

string

Eindeutige Sparten-ID.

number

integer

Sparten-Nummer.

name

string

Bezeichnung.

Payment

Feld

Typ

Beschreibung

id

string

Eindeutige Zahlungs-ID. Zahlungen werden über diesen Wert dedupliziert.

amount

decimal

Gezahlter Betrag.

paymentType

PaymentType

Verwendetes Zahlungsmittel.

fibuAccountNr

string · nullable

Fibu-Kontonummer, auf die die Zahlung gebucht wird. Für die Zuordnung auf Sachkonten im Export.

Payment type

Feld

Typ

Beschreibung

id

integer · nullable

Eindeutige Zahlungsmittel-ID.

nr

integer · nullable

Zahlungsmittel-Nummer. Der Schlüssel heißt nr, nicht number.

name

string

Bezeichnung, z. B. Bar, Kreditkarte.

Business

Feld

Typ

Beschreibung

id

string

Mandanten-ID aus der Kassendatenbank.

name

string

Firmenname.

Staff

Feld

Typ

Beschreibung

id

string

Eindeutige Mitarbeiter-ID.

number

integer

Personalnummer.

name

string

Mitarbeitername.

Table

Feld

Typ

Beschreibung

id

string

Eindeutige Tisch-ID.

number

integer

Tischnummer.

name

string

Tischbezeichnung, z. B. Bar 1, Restaurant 5.

Location

Feld

Typ

Beschreibung

id

string

APRO-Standort-ID — die locationId, auf die die Abfrage aufgelöst wurde.

name

string · nullable

Standortname.

Area

Der Bereich bzw. die Zone, in der ein Tisch steht.

Feld

Typ

Beschreibung

id

string

Eindeutige Bereichs-ID.

number

integer

Bereichsnummer.

name

string

Bezeichnung, z. B. Garten, Bar.

Payment group

Die Gruppierung, in der die Zahlungsarten zusammenlaufen — für Kassabuch- und Abschluss-Auswertungen.

Feld

Typ

Beschreibung

id

integer

Zahlungsgruppen-ID. 0, wenn nicht gesetzt.

name

string

Bezeichnung der Gruppe.

Customer

Feld

Typ

Beschreibung

id

string

Eindeutige Kunden-ID.

number

integer

Kundennummer.

name

string

Kundenname.

Hinweis: Aktuell immer null
Der Receipts-Endpunkt befüllt customer nicht — das Feld ist im
Response-Contract reserviert, wird von der Abfrage aber nicht gesetzt.
Keine Rechnungsempfänger-Logik darauf aufbauen; Kunden stattdessen über
die eigenen Endpunkte auflösen.

Beispielantwort

Response · zwei direkt aufeinanderfolgende Abrechnungen

[
  {
    "transactionId": "f03e11de-6330-4a03-a8a8-b6e5fd4e32a5",
    "transactionType": "Abrechnen",
    "receiptType": "Beleg",
    "totalAmount": 3.2,
    "tip": 0.0,
    "discount": 0.0,
    "dateTime": "03.10.2021 21:59:57",
    "business": { "id": "1", "name": "APRO Kassensysteme" },
    "location": { "id": "7236", "name": "Wien Haupthaus" },
    "staff":    { "id": "8", "number": 9500, "name": "Onlinekellner" },
    "table":    { "id": "76", "number": 76, "name": "Daniel Z" },
    "area":     { "id": "3", "number": 3, "name": "Garten" },
    "customer": null,
    "paymentGroup": { "id": 2, "name": "Kartenzahlung" },
    "payments": [
      {
        "id": "04b2d2f5-8765-41b5-ad5c-aed802dc23bc",
        "amount": 3.2,
        "paymentType": { "id": 26, "nr": 200, "name": "Smorder Kreditkarte" },
        "fibuAccountNr": "2751"
      }
    ],
    "items": [
      {
        "id": "38",
        "number": 216,
        "name": "Wiener Schnitzel",
        "uniqueId": "0f6c2c14-3f8a-4f0b-9a1e-2b7d5c8e4411",
        "externalId": null,
        "bookDate": "03.10.2021 21:59:55",
        "grossAmount": 3.2,
        "netAmount": 3.2,
        "purchasePrice": 1.1,
        "quantity": 1.0,
        "course": 2,
        "tax": { "id": "5", "value": 5.0, "name": "Mwst 5 %" },
        "category": { "id": "12", "number": 12, "name": "Hauptspeisen" },
        "sideDishes": [
          {
            "id": "512",
            "number": 9001,
            "name": "Pommes",
            "uniqueId": "b41d9a77-55c2-4e3d-8f10-6cc0a2b93d88",
            "grossAmount": 1.5,
            "netAmount": 1.36,
            "quantity": 1.0
          },
          {
            "id": "518",
            "number": 9007,
            "name": "ohne Zwiebel",
            "uniqueId": "c9a70e31-1d44-4a92-b7e5-30f8c1d6a204",
            "grossAmount": 0.0,
            "netAmount": 0.0,
            "quantity": 1.0
          }
        ]
      }
    ],
    "guestCount": 2
  },
  {
    "transactionId": "db2b90d1-ab37-4557-b169-2289c3004a0a",
    "transactionType": "Abrechnen",
    "receiptType": "Beleg",
    "totalAmount": 3.2,
    "tip": 0.0,
    "discount": 0.0,
    "dateTime": "03.10.2021 22:00:12",
    "business": { "id": "1", "name": "APRO Kassensysteme" },
    "location": { "id": "7236", "name": "Wien Haupthaus" },
    "staff":    { "id": "8", "number": 9500, "name": "Onlinekellner" },
    "table":    { "id": "76", "number": 76, "name": "Daniel Z" },
    "area":     { "id": "3", "number": 3, "name": "Garten" },
    "customer": null,
    "paymentGroup": { "id": 2, "name": "Kartenzahlung" },
    "payments": [
      {
        "id": "bb3e6300-0cc3-4922-80c4-a8a4c3530d3c",
        "amount": 3.2,
        "paymentType": { "id": 26, "nr": 200, "name": "Smorder Kreditkarte" },
        "fibuAccountNr": "2751"
      }
    ],
    "items": [
      {
        "id": "38",
        "number": 216,
        "name": "Cola 0,5",
        "uniqueId": "7d2f3b90-6c11-4c8e-93aa-5e4419c7f0d3",
        "externalId": null,
        "bookDate": "03.10.2021 22:00:11",
        "grossAmount": 3.2,
        "netAmount": 3.2,
        "purchasePrice": 0.7,
        "quantity": 1.0,
        "course": null,
        "tax": { "id": "5", "value": 5.0, "name": "Mwst 5 %" },
        "category": { "id": "20", "number": 20, "name": "Alkoholfreie Getränke" },
        "sideDishes": null
      }
    ],
    "guestCount": 1
  }
]

Dieselbe Payload als eigenständige Datei herunterladen: revenue-response.json

Transaktionstypen

Der transactionType unterscheidet, was eine Zeile repräsentiert. Für umsatzrelevante Reports auf Abrechnen filtern (und RechnungZurückholen für Rückholungen).

Typ

Bedeutung

Bonieren

Artikel auf offenen Tisch boniert.

Verschieben

Tischwechsel.

Abrechnen

Abschluss — bezahlter Beleg.

Stornieren

Stornierung einer Buchung.

RechnungZurückholen

Rücknahme einer Abrechnung.

DebitBonieren

Debit-Buchung.

ARAusstellen · ARPreview · ARNachdruck · ARNachdruckOriginal

Ausgangsrechnungs-Lifecycle.

Tagesabschluss · TagesabschlussPreview

Tagesabschluss / Vorschau.

Kellnerabschlag · KellnerabschlagPreview · KellnerabschlagUndo · Kellneruebergabe

Kellnerübergabe / Abschlag.

Startbeleg · Schlussbeleg · Nullbeleg · Monatsbeleg · JahresBeleg · Nacherfassungssammelbeleg

Gesetzliche Fiskalbelege (RKSV).

TischOeffnen · TischSchliessen · GaesteanzahlAendern · GaesteanzahlKorrigieren

Tisch-Lifecycle.

BestellmanagerBestellung · …NextState · …SetFinaleState · …Undo · …Stornieren · …Verschieben · …NaechsterGang · …Statistik

Bestellmanager-Events.

WarenkorbErstellen · WarenkorbUpdate · WarenkorbVerwerfen

Warenkorb-Lifecycle.

DatenExport · UndoDatenExport

Datenexport-Markierungen.

Kassabuch · Bargeldzählung · TerminalKassenschnitt · Lieferantabschlag · LieferantabschlagVorschau · Ansehen · Information · SchankanlageQrCode · Abrechnen HSK · Abrechnen AR · AbrechnenNachdruck

Weitere operative Ereignisse.

Umsatzberechnung

Um den umsatzrelevanten Umsatz einer Antwort zu berechnen, behält man bezahlte Abrechnungen samt Rückholungen und summiert die Netto-Positionen:

response
  .filter(x =>
    x.paymentGroup.name === "Umsatzwirksam" &&
    ["Abrechnen", "RechnungZurückholen"].includes(x.transactionType)
  )
  .map(x =>
    x.items.reduce((sum, item) => sum + item.netAmount * item.quantity, 0)
  )
  .reduce((sum, val) => sum + val, 0);

Paging

Es gibt keinen Cursor in der Antwort — nutzen Sie die deterministische transactionId der zuletzt gesehenen Zeile als afterTransactionID der nächsten Anfrage, after/before bleiben unverändert. So lange iterieren, bis die Antwort weniger Zeilen liefert als Ihr limit.

Fehler

Siehe Authentifizierung → Fehlerantworten.

Externe Bestellung API

Die Externe-Bestellung-API nimmt eine vollständig formulierte Bestellung aus einem beliebigen Fremdsystem entgegen — Lieferaggregator, White-Label- App, Kiosk, Voice-Agent — und steuert sie in die laufende APRO-Kassa am Zielstandort ein. Ein einziger POST genügt; die Bestellung erscheint sofort im Bestellmanager.

  • Endpunkt: POST https://my.apro.at/api/v1/external/order
  • Auth: API-Key im x-api-key
  • Content type: application/json

Bestellung einem Standort zuordnen

Die Bestellung muss genau einem Standort zugeordnet sein. Geben Sie einen der folgenden Werte mit — der erste nicht-leere Wert gewinnt:

  1. LocationID — numerische ID
  2. LocationShortCode — Kurz-Identifier (z. B. vienna-main)
  3. SpotID — konkreter Tisch/Zimmer/Abholpunkt (Standort wird abgeleitet)
  4. QRCode — gescannter APRO-QR-Code (Standort und Spot werden abgeleitet)

Fehlen alle, wird die Anfrage abgelehnt.

Request-Body

Feld

Typ

Pflicht

Beschreibung

guid

string

—

Idempotenz-Schlüssel. Kommt derselbe guid zweimal, wird das Duplikat ignoriert.

locationId

int?

◻︎

Siehe Routing oben.

locationShortCode

string

◻︎

Siehe Routing oben.

spotId

int?

—

ID des Bestellziels (Tisch, Zimmer, Abhol-, Lieferstandort — alles Spots, nur unterschiedliche Typen). Entspricht spot.id der Externer-Katalog-API.

spotNumber

int?

—

Nummer des Spots, wenn die ID unbekannt ist. Aufgelöst über spot.number.

spotName

string

—

Fallback, wenn weder spotId noch spotNumber auflöst. Lookup über spot.name — bevorzugt einen Spot vom Typ Table, danach beliebiger Spot am Standort.

roomNumber

int?

—

Nummer des Zimmer-Spots für Hotel-/Zimmerabrechnungs-Flows. Aufgelöst gegen einen SpotType.Room-Spot über spot.number (verschoben um RoomNrSpotNrOffset des Standorts); Fallback auf spot.alternativeName gegen die rohe Zimmernummer. Bei Treffer wird spotId gesetzt (falls noch nicht vorhanden) und die Bestellung an die aktive Reservierung des Zimmers gebunden.

roomId

int?

—

Für zukünftige Erweiterungen reserviert. Wird aktuell akzeptiert, aber ignoriert — für ein Zimmer den spotId eines Room-Spots verwenden.

userInfo

UserInfo

✅

Wer bestellt — email ist Pflicht.

deliveryInfo

DeliveryInfo

—

Lieferadresse. Pflicht bei Zustellung.

takeaway

bool

✅

true für Mitnehmen, false für Vor Ort.

note

string

—

Freitext-Notiz des Gastes (Allergene, Sonderwünsche).

qrCode

string

—

Siehe Routing.

entries

[Entry]

✅

Bestellte Artikel.

preOrderTime

long?

—

Vorbestellzeitpunkt, Unix-Zeit in Millisekunden UTC.

shortCode

string

—

Abhol-/Lieferreferenz (z. B. zw1356).

tip

decimal?

—

Trinkgeldhöhe.

isPaid

bool

—

true, wenn die Bestellung bereits extern bezahlt wurde.

grandTotal

decimal?

✅

Summe aller Artikel + Tip. Wird serverseitig gegengeprüft.

Entry

Feld

Typ

Pflicht

Beschreibung

productNr

string

✅

Produktnummer in APRO.

name

string

—

Anzeigename — rein informativ; APRO vertraut auf productNr.

preOrderTime

long?

—

Vorbestellzeit pro Artikel.

quantity

double

✅

Bestellmenge.

price

double?

—

Übersteuert den Systempreis dieser Position (Rabatte, Kampagnen).

note

string

—

Artikel-Notiz (z. B. Ohne Zwiebeln).

addons

[Addon]

—

Zusatzoptionen.

Addon

Feld

Typ

Pflicht

Beschreibung

productNr

string

✅

Produktnummer des Addons.

name

string

—

Anzeigename.

quantity

double

✅

Menge.

price

double?

—

Übersteuert den Systempreis.

UserInfo

Feld

Typ

Pflicht

Beschreibung

firstName

string

—


lastName

string

—


email

string

✅

Kontakt-E-Mail für Belege und Status-Updates.

phone

string

—


locale

string

—

IETF-Locale, z. B. de-DE.

nickname

string

—

Kurzname für Besteller/Abholer.

DeliveryInfo

Feld

Typ

Beschreibung

name

string

Bezeichnung der Lieferadresse.

companyName

string

Firmenname.

city

string

Ort.

zip

string

PLZ.

street

string

Straße.

country

string

Land.

floor

string

Stockwerk.

stairway

string

Stiege.

door

string

Türe.

note

string

Hinweis zur Lieferadresse.

Beispiele

Minimalbestellung

POST /api/v1/external/order

{
  "locationShortCode": "vienna-main",
  "takeaway": true,
  "userInfo": { "email": "guest@example.com" },
  "entries": [{ "productNr": "123", "quantity": 1 }],
  "grandTotal": 9.99
}

Download: external-order-minimal.json

Vollständige Bestellung mit Lieferung und Beilagen

POST /api/v1/external/order

{
  "guid": "123e4567-e89b-12d3-a456-426614174000",
  "locationId": 101,
  "spotId": 15,
  "spotNumber": 10,
  "shortCode": "zw1356",
  "userInfo": {
    "firstName": "Max",
    "lastName": "Mustermann",
    "email": "max.mustermann@example.com",
    "phone": "+491234567890",
    "locale": "de-DE",
    "nickname": "Max"
  },
  "takeaway": false,
  "note": "Bitte schnell liefern",
  "qrCode": "QR1234567890",
  "deliveryInfo": {
    "name": "Max Mustermann",
    "city": "Wien",
    "zip": "1010",
    "street": "Stephansplatz 1",
    "country": "AT",
    "floor": "3",
    "stairway": "A",
    "door": "12"
  },
  "entries": [
    {
      "productNr": "123",
      "name": "Wiener Schnitzel",
      "quantity": 2,
      "price": 9.99,
      "note": "Extra scharf",
      "addons": [
        { "productNr": "501", "name": "Pommes", "quantity": 2, "price": 3.5 }
      ]
    },
    { "productNr": "31231", "name": "Burger Classic", "quantity": 1, "note": "Ohne Zwiebeln" }
  ],
  "preOrderTime": 1723574400000,
  "tip": 1.2,
  "isPaid": true,
  "grandTotal": 28.18
}

Download: external-order-full.json

Idempotenz

Setzen Sie guid immer auf eine stabile, client-generierte UUID. APRO nutzt sie zur Deduplizierung bei Retries — geht eine Antwort verloren und Sie senden erneut, ist der zweite POST ein No-Op und liefert Erfolg.

Preisberechnung

APRO berechnet jede Position serverseitig aus productNr neu. price auf einem Entry oder Addon greift nur, wenn explizit übergeben; grandTotal wird mit dem Server-Total abgeglichen, inkonsistente Payloads werden abgelehnt. So wird der Wert zugleich Sicherheitsnetz und Vehikel für Kampagnen-Rabatte.

Externer Katalog API

Der Externe-Katalog-API bietet eine schlanke, nur lesende Sicht auf die Stammdaten eines APRO-Standorts — Artikel, Warengruppen und Spots (Tische, Zimmer, Abholpunkte) — zur Verwendung in White-Label- Apps, Kiosks, externen Speisekarten und Partnerintegrationen.

Die drei Endpunkte teilen sich dieselben Konventionen: Scope per Standort, verschachtelte DTO-Struktur, Filterung über Query-Parameter und Paginierung.

  • Host: my.apro.at
  • Auth: API-Key im x-api-key
  • Content type: application/json
Tipp: In Postman ausprobieren
Lade die fertige Collection herunter:
apro-external-catalog.postman_collection.json.
In Postman importieren, im Variables-Tab der Collection apiKey und
locationShortCode setzen, dann Send drücken.
Achtung: Filter verwenden eine Bracket-Syntax
Alle Filterfelder — inklusive des Pflichtfeldes locationId /
locationShortCode — müssen als filter['<name>']=<value> übergeben
werden, nicht als gewöhnliche Query-Parameter. Schlichtes
?locationShortCode=… wird stillschweigend ignoriert; die Anfrage
schlägt dann mit „location not resolved" fehl. Details unter
Filterung.

Endpunkte

Methode

URL

Liefert

GET

/api/v1/external/product

Paginierte Liste von Product

GET

/api/v1/external/product-category

Paginierte Liste von ProductCategory

GET

/api/v1/external/spot

Paginierte Liste von Spot

Gemeinsame Konventionen

Standort-Scope

Jede Anfrage muss genau einen Standort adressieren. Geben Sie einen der folgenden Werte mit:

  1. locationId — numerische ID
  2. locationShortCode — Kurz-Identifier (z. B. vienna-main)

Lässt sich aus keinem von beiden ein Standort auflösen, wird die Anfrage mit 400 Bad Request abgelehnt.

Paginierung

Query-Parameter

Default

Beschreibung

skip

0

Anzahl der zu überspringenden Datensätze.

take

50

Seitengröße. Limit 500.

Jede Antwort steckt in einem Paged-Envelope:

{
  "page": { "offset": 0, "limit": 50, "total": 1234 },
  "data": [ /* ... */ ]
}
  • offset und limit spiegeln die Anfrage.
  • total ist die Gesamtanzahl aller passenden Datensätze über alle Seiten — Grundlage für skip/take-Schleifen.

Filterung

Filter werden als bracketed Query-Parameter unter dem Schlüssel filter übergeben — filter['<name>']=<value>. Schlichtes ?<name>=<value> wird ignoriert. Paginierung (skip, take) bleibt als gewöhnliche Query-Parameter.

Listen-Filter (productIds, gtins, externalSpotIds, …) werden kommagetrennt übergeben. Literale Kommas mit \, escapen.

GET /api/v1/external/product?filter['locationShortCode']=vienna-main
  &filter['productNrs']=A-100,A-200
  &filter['isVisible']=true
  &filter['search']=schnitzel
  &skip=0&take=100

Hinweise:

  • Der Klammer-Key ist case-insensitive — filter['LocationShortCode'] und filter['locationShortCode'] sind äquivalent.
  • filter["…"] (doppelte Anführungszeichen, URL-kodiert als %22) funktioniert ebenfalls.
  • Unbekannte Filterparameter werden ignoriert.

Artikel

GET https://my.apro.at/api/v1/external/product

Query-Parameter

Parameter

Typ

Beschreibung

locationId

int?

Pflicht (oder locationShortCode).

locationShortCode

string

Pflicht (oder locationId).

cashboxId

int?

Auf eine Kassa einschränken. Muss zum Standort gehören.

search

string

Volltextsuche über Name, externer Name, productNr, GTIN und externe Artikel-ID.

productIds

int[]

Filter nach APRO-Artikel-ID.

productNrs

string[]

Filter nach productNr (der Wert, der bei External-Order-Einträgen zurückgegeben wird).

gtins

string[]

Filter nach GTIN (Barcode).

externalProductIds

string[]

Filter nach externer Artikel-ID (Drittsystem-Referenz).

isVisible

bool?

Filter nach Sichtbarkeit.

isLocked

bool?

Filter nach Sperr-Flag.

Product

Feld

Typ

Beschreibung

id

int

APRO-Cloud-Artikel-ID.

productNr

string

Identifier, mit dem die Bestell-Pipeline diesen Artikel auflöst. Diesen Wert direkt als productNr des Eintrags beim Anlegen einer Bestellung via Externe-Bestellung-API verwenden. Basiert auf der POS-Artikelnummer.

gtin

string

Barcode / GTIN.

externalProductId

string

POS-Artikel-ID — die im lokalen Kassensystem vergebene ID. Informativ; nicht der Wert, gegen den die External-Order-productNr aufgelöst wird.

name

string

Anzeigename.

externalName

string

Name für externe Oberflächen — Fallback auf name, wenn leer.

subtitle

string

Kurz-Untertitel.

description

string

Ausführliche Beschreibung.

pricing

Pricing

Preis- und Steuerinfo.

cashbox

ProductCashbox

Kassa / Eltern-Artikel / Produktionsplatz.

media

Media

Bild-URLs.

flags

Flags

Sichtbarkeit, Sperre, Review-Status.

nutrition

Nutrition

Menge, Kalorien, Allergene, Altersgrenze.

productGroupExternalIds

string[]

Externe IDs der Warengruppen, denen der Artikel angehört.

localizations

Localization[]

Gepflegte Übersetzungen der Artikeltexte und des Bildes, ein Eintrag je Sprache. Leer, wenn keine vorhanden sind.

Pricing

Feld

Typ

Beschreibung

defaultPrice

double?

Standardpreis.

referencePrice

double?

Referenz-/Streichpreis.

taxRateId

int?

APRO-Steuersatz-ID.

allowClientPrice

bool

Ob Clients den Preis überschreiben dürfen.

ProductCashbox

Feld

Typ

Beschreibung

cashboxId

int

Zugehörige Kassa.

productionPlaceId

int?

Küche / Bar, in der zubereitet wird.

parentProductId

int?

Bei Varianten / Template-Ableitungen gesetzt.

Media

Feld

Typ

Beschreibung

imageUrl

string

Öffentliche Bild-URL.

Flags

Feld

Typ

Beschreibung

isVisible

bool

Sichtbar in Storefronts.

isLocked

bool

Gegen automatische Sync-Änderungen gesperrt.

queuedForReview

bool

Wartet auf manuelles Review.

Nutrition

Feld

Typ

Beschreibung

amount

double?

Nettomenge.

amountUnit

string

Einheit, z. B. g, ml.

calories

double?

Kalorien pro amount.

allergens

int (Flags)

Bitflag der deklarierten Allergene.

minimumAge

int

Mindestkäuferalter (0, wenn keines).

ingredients

[Ingredient]

Deklarierte Zutatenliste. Leer-Array, wenn nicht gepflegt.

nutritionalValues

[NutritionalValue]

Strukturierte Nährwerte. Leer-Array, wenn nicht gepflegt.

Ingredient

Feld

Typ

Beschreibung

name

string

Zutaten-Bezeichnung (z. B. Lamm, Weizenmehl).

NutritionalValue

Feld

Typ

Beschreibung

name

string

Wert-Bezeichnung (z. B. Gewicht, Kalorien, Fett).

unit

string

Einheit, freier Text (z. B. g, ml, kcal).

value

string

Numerischer Wert, als String serialisiert, um das Quellformat zu erhalten. Client-seitig parsen, falls nötig.

belongsTo

int?

Optionale Referenz auf eine Unterkomponente / Variante (null, wenn der Wert für das Gesamtprodukt gilt).

Localization

Feld

Typ

Beschreibung

locale

string

Sprach- / Culture-Schlüssel der Übersetzung (z. B. en, it, de-DE).

name

string

Übersetzter Anzeigename.

subtitle

string

Übersetzter Untertitel.

description

string

Übersetzte ausführliche Beschreibung.

imageUrl

string

Sprachspezifisches Bild, sofern gepflegt.

name, subtitle, description und media.imageUrl auf oberster Ebene enthalten weiterhin die Standardwerte (Deutsch) — localizations ersetzt sie nicht, sondern ergänzt nur die vorhandenen Übersetzungen. Einträge ohne Sprachschlüssel werden weggelassen, und einzelne Felder eines Eintrags können null sein, wenn genau dieser Text nicht übersetzt wurde. Zur Anzeige die gewünschte Sprache in localizations suchen und auf das Feld der obersten Ebene zurückfallen.

Beispiel

GET /api/v1/external/product?filter['locationShortCode']=vienna-main&filter['isVisible']=true&take=2
{
  "page": { "offset": 0, "limit": 2, "total": 1247 },
  "data": [
    {
      "id": 9001,
      "productNr": "A-100",
      "gtin": "9001234567890",
      "externalProductId": "wiener-schnitzel",
      "name": "Wiener Schnitzel",
      "externalName": "Wiener Schnitzel",
      "subtitle": "Mit Erdäpfelsalat",
      "description": "Klassisches Wiener Schnitzel vom Kalb.",
      "pricing": {
        "defaultPrice": 22.9,
        "referencePrice": null,
        "taxRateId": 3,
        "allowClientPrice": false
      },
      "cashbox": { "cashboxId": 17, "productionPlaceId": 2, "parentProductId": null },
      "media": { "imageUrl": "https://cdn.smorder.at/locations/7236/p/9001.jpg" },
      "flags": { "isVisible": true, "isLocked": false, "queuedForReview": false },
      "nutrition": {
        "amount": 250,
        "amountUnit": "g",
        "calories": 720,
        "allergens": 67,
        "minimumAge": 0,
        "ingredients": [{ "name": "Kalb" }, { "name": "Weizenmehl" }, { "name": "Ei" }],
        "nutritionalValues": [
          { "name": "Gewicht", "unit": "g", "value": "250", "belongsTo": null },
          { "name": "Fett",    "unit": "g", "value": "30",  "belongsTo": null }
        ]
      },
      "productGroupExternalIds": ["mains", "austrian"],
      "localizations": [
        {
          "locale": "en",
          "name": "Viennese Schnitzel",
          "subtitle": "With potato salad",
          "description": "Classic Viennese veal schnitzel.",
          "imageUrl": null
        },
        {
          "locale": "it",
          "name": "Cotoletta alla viennese",
          "subtitle": null,
          "description": null,
          "imageUrl": null
        }
      ]
    }
  ]
}

Download: external-products-response.json

Warengruppen

GET https://my.apro.at/api/v1/external/product-category

Warengruppen entsprechen den APRO Product Groups — die kassenbezogene Gruppierung, die Reports, Steuersätze und Druckreihenfolge steuert. Das Feld productGroupExternalIds auf einem Artikel referenziert genau die externalId, die hier zurückkommt — damit lassen sich beide Antworten ohne zusätzliche Lookups verbinden.

Query-Parameter

Parameter

Typ

Beschreibung

locationId

int?

Pflicht (oder locationShortCode).

locationShortCode

string

Pflicht (oder locationId).

cashboxId

int?

Auf eine Kassa einschränken. Muss zum Standort gehören.

search

string

Volltextsuche über Name, externer Name und externe ID.

categoryIds

int[]

Filter nach APRO-Warengruppen-ID.

externalIds

string[]

Filter nach externer ID.

ProductCategory

Feld

Typ

Beschreibung

id

int

APRO-Cloud-Warengruppen-ID.

externalId

string

POS-Warengruppen-ID — die im lokalen Kassensystem vergebene ID.

name

string

Anzeigename.

externalName

string

Name für externe Oberflächen.

cashbox

CategoryCashbox

Kassa / Lieferant.

tax

CategoryTax

Steuersatz.

localizations

CategoryLocalization[]

Gepflegte Übersetzungen der Warengruppentexte und des Bildes, ein Eintrag je Sprache. Leer, wenn keine vorhanden sind.

CategoryCashbox

Feld

Typ

Beschreibung

cashboxId

int?

Zugehörige Kassa.

supplierId

int?

Standard-Lieferant für Artikel der Warengruppe.

CategoryTax

Feld

Typ

Beschreibung

taxRateId

int?

Standard-Steuersatz für Artikel der Warengruppe.

vat

double

USt-Prozentsatz (z. B. 20 für 20 %).

CategoryLocalization

Feld

Typ

Beschreibung

locale

string

Sprach- / Culture-Schlüssel der Übersetzung (z. B. en, it, de-DE).

name

string

Übersetzter Anzeigename.

description

string

Übersetzte Beschreibung.

imageUrl

string

Sprachspezifisches Bild, sofern gepflegt.

Es gelten dieselben Regeln wie bei Artikeln: name auf oberster Ebene bleibt der deutsche Standardwert, localizations ergänzt nur das, was vorhanden ist.

Beispiel

GET /api/v1/external/product-category?filter['locationShortCode']=vienna-main
{
  "page": { "offset": 0, "limit": 50, "total": 14 },
  "data": [
    {
      "id": 41,
      "externalId": "mains",
      "name": "Hauptspeisen",
      "externalName": "Mains",
      "cashbox": { "cashboxId": 17, "supplierId": null },
      "tax": { "taxRateId": 3, "vat": 10 },
      "localizations": [
        { "locale": "en", "name": "Mains", "description": null, "imageUrl": null },
        { "locale": "it", "name": "Secondi", "description": null, "imageUrl": null }
      ]
    },
    {
      "id": 42,
      "externalId": "drinks-alcoholic",
      "name": "Getränke (Alkohol)",
      "externalName": "Alcoholic drinks",
      "cashbox": { "cashboxId": 17, "supplierId": null },
      "tax": { "taxRateId": 1, "vat": 20 },
      "localizations": []
    }
  ]
}

Download: external-product-categories-response.json

Spots

GET https://my.apro.at/api/v1/external/spot

Spots sind die physischen / logischen Ziele, an die ein Gast bestellen kann: Tische, Zimmer, Abholtheken, Lieferzonen, Drive-in.

Query-Parameter

Parameter

Typ

Beschreibung

locationId

int?

Pflicht (oder locationShortCode).

locationShortCode

string

Pflicht (oder locationId).

areaId

int?

Auf einen Bereich / ein Zimmer einschränken.

search

string

Volltextsuche über Name, Alternativname, externe Spot-ID und QR-Code.

spotIds

int[]

Filter nach APRO-Spot-ID.

externalSpotIds

string[]

Filter nach externer Spot-ID.

type

enum

Spot / Table / Room / Pickup / Delivery / …

visibleInApp

bool?

Filter nach App-Sichtbarkeit.

supportsTableOrder

bool?

Filter nach Tischbestell-Fähigkeit.

Spot

Feld

Typ

Beschreibung

id

int

APRO-Cloud-Spot-ID.

externalSpotId

string

POS-Spot-ID — die im lokalen Kassensystem vergebene ID.

number

int?

Numerisches Label (z. B. Tischnummer).

name

string

Anzeigename.

alternativeName

string

Sekundärer Name für manche Oberflächen.

description

string

Freitext-Beschreibung.

qrCode

string

QR-Code-Payload des Spots.

type

enum

Spot-Typ.

capacity

int?

Sitzplätze (Tisch) / Maximalgäste (Zimmer).

location

SpotLocation

Zugehöriger Standort.

area

SpotArea

Zugehöriger Bereich, falls vorhanden.

geo

SpotGeo

Geokoordinaten. null, wenn beide Werte fehlen.

flags

SpotFlags

Fähigkeits-Flags.

SpotLocation

Feld

Typ

Beschreibung

locationId

int

Numerische Standort-ID.

locationShortCode

string

Kurzcode für Cross-API-Joins.

SpotArea

Feld

Typ

Beschreibung

areaId

int

APRO-Bereichs-ID.

name

string

Anzeigename des Bereichs.

externalAreaId

string

Externe Referenz des Bereichs.

SpotGeo

Feld

Typ

Beschreibung

latitude

double?

WGS84-Breite.

longitude

double?

WGS84-Länge.

SpotFlags

Feld

Typ

Beschreibung

visibleInApp

bool

Sichtbar in Storefronts.

supportsTableOrder

bool

Akzeptiert Tischbestellungen.

supportsCashPayment

bool

Akzeptiert Bargeld.

defaultToTakeAway

bool

Default-Bestellmodus ist Takeaway.

displayAsButton

bool

Als Button statt Listeneintrag darstellen.

Beispiel

GET /api/v1/external/spot?filter['locationShortCode']=vienna-main&filter['type']=Table&filter['visibleInApp']=true&take=2
{
  "page": { "offset": 0, "limit": 2, "total": 38 },
  "data": [
    {
      "id": 1501,
      "externalSpotId": "table-01",
      "number": 1,
      "name": "Tisch 1",
      "alternativeName": null,
      "description": null,
      "qrCode": "QR-01",
      "type": "Table",
      "capacity": 4,
      "location": { "locationId": 7236, "locationShortCode": "vienna-main" },
      "area": { "areaId": 11, "name": "Gastgarten", "externalAreaId": "garden" },
      "geo": null,
      "flags": {
        "visibleInApp": true,
        "supportsTableOrder": true,
        "supportsCashPayment": true,
        "defaultToTakeAway": false,
        "displayAsButton": false
      }
    }
  ]
}

Download: external-spots-response.json

Cross-API-Joins

Die drei Endpunkte sind so geschnitten, dass sie sich client-seitig verbinden lassen:

  • product.productNr ↔ productNr (Entry / Addon) der Externe-Bestellung-API — case-insensitive, gematcht ausschließlich gegen Artikel auf der Primär-Kassa des Standorts. Wenn productNr nicht auflöst, greift im Bestell-Pipeline ein Fallback über den name-Wert des Entries gegen product.name.
  • product.productGroupExternalIds[i] ↔ productCategory.externalId
  • product.cashbox.cashboxId / productCategory.cashbox.cashboxId ↔ cashboxId-Filter auf beiden Endpunkten
  • spot.location.locationShortCode ↔ locationShortCode-Filter überall
  • spot.id ↔ SpotID der External-Order-API — die id des Spots direkt zurückgeben, um eine Bestellung an denselben Spot zurückzurouten, von dem sie gelesen wurde. spot.qrCode entspricht dem QRCode-Feld der External-Order-API.

Fehler

Status

Bedeutung

400 Bad Request

Weder locationId noch locationShortCode lieferten einen gültigen Standort.

401 Unauthorized

x-api-key fehlt oder ist ungültig.

403 Forbidden

Key nicht für den Standort autorisiert.

429 Too Many Requests

Rate-Limit — mit Jitter zurücknehmen und erneut versuchen.

Menükarten API

Die Menükarten-API liefert die öffentliche Menüstruktur für White-Label-Apps, Selbstbedienungskiosks und QR-Ordering-Flows. Das Lesen erfordert keine Authentifizierung; jede schreibende Aktion schon.

  • Host: my.apro.at
  • Content type: application/json

Menü abrufen

GET https://my.apro.at/api/v1/locations/{locationId}/menus

Die Antwort ist ein Baum. Ebenen von außen nach innen:

  1. Menu — oberste Gruppierung (z. B. Speisen, Getränke, Angebote).
  2. Category — Abschnitt innerhalb eines Menüs (z. B. Hauptspeisen).
  3. MenuEntry — konkreter verkaufbarer Artikel.
  4. MenuEntryFeature — Beilagen-/Modifier-Gruppe zu einem Artikel.
  5. MenuEntryFeatureValue — einzelne Option innerhalb einer Gruppe.
  6. Hours — Gültigkeitsfenster, die an Menüs und Kategorien hängen.

Beispielantwort (gekürzt)

GET /api/v1/locations/{locationId}/menus

[
  {
    "name": "Speisen",
    "badge": 7,
    "imageUrl": "https://cdn.smorder.at/locations/7236/menus/speisen.jpg",
    "description": "Klassiker aus der österreichischen Küche.",
    "priceName": "Hauspreis",
    "orderableHours": [
      { "days": 254, "startTime": "11:00", "endTime": "22:00" }
    ],
    "categories": [
      {
        "name": "Hauptspeisen",
        "menuEntries": [
          {
            "menuEntryID": 4001,
            "name": "Wiener Schnitzel",
            "badge": 7,
            "price": 22.9,
            "features": [
              {
                "name": "Beilage",
                "groupName": "Beilage",
                "minValuesRequired": 1,
                "maxValuesAllowed": 1,
                "valuesForFree": 1,
                "values": [
                  { "name": "Erdäpfelsalat", "priceAdjustment": 0 },
                  { "name": "Pommes frites",  "priceAdjustment": 0 },
                  { "name": "Reis",           "priceAdjustment": 0 }
                ]
              },
              {
                "name": "Ohne",
                "isDeselectFeature": true,
                "values": [
                  { "name": "Preiselbeeren", "priceAdjustment": 0 }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]

Vollständiges, ungekürztes Beispiel herunterladen: menu-response.json

1. Menu

Oberste Gruppierung im Menübaum; enthält Kategorien. Beispiele: Speisen, Getränke, Angebote.

Feld

Typ

Beschreibung

name

string

Anzeigename.

badge

enum

Optischer Badge — siehe Badges.

imageUrl

string

URL zum Titelbild.

description

string

Beschreibungstext.

categories

Category[]

Kategorien in diesem Menü.

orderableHours

Hours[]

Zeiten, in denen Bestellungen möglich sind.

preOrderableHours

Hours[]

Zeiten, für die Vorbestellungen möglich sind.

deliverableHours

Hours[]

Zeiten, in denen Zustellung möglich ist.

priceName

string

Zu verwendende Preisebene (z. B. Hauspreis, Happy Hour).

Mutationen (Auth erforderlich):

Aktion

Methode

Pfad

Anlegen

POST

/api/v1/customer/locations/{locationId}/menu

Aktualisieren

PUT

/api/v1/customer/menus/{menuId}

Löschen

DELETE

/api/v1/customer/menus/{menuId}

2. Category

Unterpunkt eines Menüs. Beispiele: Alkoholfreie Getränke, Bier, Hauptspeisen, Nachspeisen.

Feld

Typ

Beschreibung

name

string

Anzeigename.

badge

enum

Siehe Badges.

imageUrl

string

URL zum Titelbild.

description

string

Beschreibungstext.

menuEntries

MenuEntry[]

Einträge in dieser Kategorie.

orderableHours

Hours[]

Bestellzeiten.

preOrderableHours

Hours[]

Vorbestellzeiten.

deliverableHours

Hours[]

Lieferzeiten.

Mutationen (Auth erforderlich):

Aktion

Methode

Pfad

Anlegen

POST

/api/v1/customer/menus/{menuId}/category

Aktualisieren

PUT

/api/v1/customer/menus/{menuId}/category/{id}

Löschen

DELETE

/api/v1/customer/menus/{menuId}/category/{id}

3. MenuEntry

Konkret verkaufbarer Artikel. Beispiele: Wiener Schnitzel, Kaiser Bier.

Feld

Typ

Beschreibung

menuEntryID

integer

Eindeutige ID; wird mit der Bestellung zurückgeschickt.

name

string

Artikelname.

badge

enum

Siehe Badges.

imageUrl

string

URL zum Artikelbild.

subtitle

string

Kurzbeschreibung.

description

string

Beschreibungstext.

features

MenuEntryFeature[]

Beilagen-Gruppen (Größen, Beilagen …).

price

money

Verkaufspreis.

Mutationen (Auth erforderlich):

Aktion

Methode

Pfad

Anlegen

POST

/api/v1/customer/categories/{categoryId}/menuEntry

Aktualisieren

PUT

/api/v1/customer/menuEntries/{id}

Löschen

DELETE

/api/v1/customer/menuEntries/{id}

4. MenuEntryFeature

Beilagen-/Modifier-Gruppe eines MenuEntry. Beispiele: Gebindegröße, Beilage zur Hauptspeise.

Feld

Typ

Beschreibung

name

string

Bezeichnung der Beilage.

description

string

Beschreibungstext.

groupName

string

Gruppierungsschlüssel für seitenweise Anzeige.

isDeselectFeature

boolean

true markiert eine Abwahl-Option (z. B. Ohne Zwiebeln).

maxValuesAllowed

number

Maximalanzahl auswählbarer Optionen.

minValuesRequired

number

Mindestanzahl auswählbarer Optionen.

valuesForFree

number

Anzahl der Optionen ohne Aufpreis.

values

MenuEntryFeatureValue[]

Verfügbare Optionen.

Mutationen (Auth erforderlich):

Aktion

Methode

Pfad

Anlegen

POST

/api/v1/menuentryfeature

Aktualisieren

PUT

/api/v1/menuentryfeature/{featureId}

Löschen

DELETE

/api/v1/menuentryfeature/{featureId}

5. MenuEntryFeatureValue

Einzelne Ausprägung einer Beilage. Beispiele: Bier 0,3 l, Bier 0,5 l, Pommes, Kartoffeln.

Feld

Typ

Beschreibung

name

string

Bezeichnung.

description

string

Beschreibungstext.

imageUrl

string

URL zum Optionsbild.

minAmount

number

Mindeststückzahl bei Auswahl.

maxAmount

number

Maximale Stückzahl.

freeAmount

number

Stückzahl ohne Aufpreis.

priceAdjustment

number

Preisanpassung pro Stück.

Mutationen (Auth erforderlich):

Aktion

Methode

Pfad

Anlegen

POST

/api/v1/menuentryfeaturevalue

Aktualisieren

PUT

/api/v1/menuentryfeaturevalue/{featureValueId}

Löschen

DELETE

/api/v1/menuentryfeaturevalue/{featureValueId}

6. Hours

Gültigkeitsfenster an Menüs oder Kategorien. Sie steuern, wann bestellt, vorbestellt oder geliefert werden darf.

Feld

Typ

Beschreibung

days

enum bitmask

Bitmaske der Wochentage — siehe unten.

startDate

date

Absolutes Startdatum.

endDate

date

Absolutes Enddatum.

startTime

daytime

Startzeit an gültigen Tagen.

endTime

daytime

Endzeit an gültigen Tagen.

exclude

boolean

Fenster ausschließen statt einschließen.

Wochentags-Bitmaske:

Tag

Bit

Wert

Montag

1 << 1

2

Dienstag

1 << 2

4

Mittwoch

1 << 3

8

Donnerstag

1 << 4

16

Freitag

1 << 5

32

Samstag

1 << 6

64

Sonntag

1 << 7

128

Beispiel: Mo–Fr ergibt 2 + 4 + 8 + 16 + 32 = 62.

Badges

Optische Hervorhebungen für Menüs, Kategorien und Einträge:

Bezeichnung

Wert

NEU

1

Empfehlung

2

Reduziert

3

Coming Soon

4

Unser Tip

5

Special

6

Bestseller

7

HOT

8

SALE

9

Hotel API (PMS)

Die Hotel-API ist die bidirektionale Brücke zwischen einer APRO-Kassa und einem Property-Management-System (PMS). Das PMS spielt Reservierungen in die Kassa ein, damit Gäste aufs Zimmer anschreiben können; die Kassa liefert die Zimmerumsätze an das PMS zurück, damit sie auf der Hotelrechnung landen.

Typischer Ablauf

PMS                                       APRO POS
 │ 1. POST  /pms/reservations  ─────────▶ │   (Reservierungen upsert)
 │                                        │
 │                                        │  Gäste schreiben aufs Zimmer …
 │                                        │
 │ 2. GET   /pms/charges?hotelId=…  ─────▶│
 │ ◀─── Charges (optional inkl. Receipts) │
 │                                        │
 │ 3. POST  /pms/confirm-charges  ───────▶│   (ack; keine Wiederholung)
 │                                        │
 │ 4. DELETE /pms/reservations?…  ───────▶│   (bei Check-out / Abschluss)

Schritt 2 ist die Polling-Schleife des PMS. Schritte 1 und 4 werden durch Ereignisse im PMS ausgelöst. Schritt 3 muss jeder erfolgreichen Abfrage folgen — sonst werden dieselben Charges beim nächsten Poll wieder geliefert.

1. Reservierungen upserten

Den kompletten aktiven Reservierungsstand ins POS übergeben. Schlüssel sind hotelId + Id + guestId; bestehende Zeilen werden in-place aktualisiert.

POST https://cloudgateway.apro.at/hotelservice/api/v1/pms/reservations

Request-Body

Feld

Typ

Pflicht

Beschreibung

reservations

Reservation[]

✅

Eine oder mehrere Reservierungen.

Reservation

Feld

Typ

Pflicht

Beschreibung

Id

string

✅

Reservierungs-ID im PMS.

hotelId

string

✅

PMS-Mandant / Hotel.

guestId

string

✅

Gast-ID im PMS.

roomNr

string

✅

Zugewiesene Zimmernummer.

title

string

—

Anrede (Mr., Mrs. …).

firstName

string

—


lastName

string

—


arrival

ISO 8601

✅

Check-in-Zeitpunkt.

departure

ISO 8601

✅

Check-out-Zeitpunkt.

nrOfIndividuals

integer

—

Personenzahl — für Covers/Buffet-Regeln.

identifiers

Identifier[]

—

Keycard-UIDs, NFC-Tokens, Zimmer-Tablets — alle Kennungen, mit denen das POS eine Zimmerbuchung erkennen soll.

Identifier

Feld

Typ

Beschreibung

value

string

Wert der Kennung (Keycard-UID, Token).

Beispielanfrage

POST /hotelservice/api/v1/pms/reservations

{
  "reservations": [
    {
      "Id": "Aabc123",
      "hotelId": "1",
      "guestId": "Abc123",
      "roomNr": "Abc123",
      "title": "Mr.",
      "firstName": "Max",
      "lastName": "Mustermann",
      "arrival": "2026-04-02T14:00:00",
      "departure": "2026-04-04T14:00:00",
      "nrOfIndividuals": 1,
      "identifiers": [{ "value": "keycard-uid" }]
    }
  ]
}

Download: hotel-reservations-upsert.json

Beispielantwort

200 OK

{
  "newReservationsCount": 0,
  "checkedOutReservationsCount": 0,
  "updatedReservationsCount": 1,
  "state": 1,
  "isSuccessfull": true,
  "errorCode": 0,
  "message": null,
  "context": null
}

Download: hotel-reservations-response.json

Tipp
Immer den vollständigen aktiven Stand übertragen, keine Deltas.
checkedOutReservationsCount zeigt an, wie viele bisher bekannte
Reservierungen nicht mehr in der Payload waren — diese werden
automatisch ausgecheckt.

2. Charges abfragen

Holt alle seit dem letzten bestätigten Poll auf Zimmer gebuchten Umsätze.

GET https://cloudgateway.apro.at/hotelservice/api/v1/pms/charges
    ?hotelId=1
    &excludeAlreadyExported=true

Query-Parameter

Name

Typ

Pflicht

Beschreibung

hotelId

string

✅

PMS-Mandant / Hotel.

excludeAlreadyExported

bool

—

true = nur noch nicht bestätigte Einträge (der normale Polling-Modus). false = das gesamte konfigurierte Fenster erneut.

Antwortstruktur

Jedes Element hat ein chargeType, das bestimmt welche Felder befüllt sind:

chargeType

Bedeutung

payments vorhanden?

Charge

Offene Buchung aufs Zimmer (Gast hat noch nicht gezahlt — POS bucht aufs Zimmer-Folio).

nein

Receipt

Geschlossener Beleg (Gast hat an der Kassa bezahlt; PMS kann ignorieren oder abgleichen).

ja

Top-Level:

Feld

Typ

Beschreibung

page.limit

integer

Angewandte Seitengröße.

page.offset

integer

Offset im Backlog.

page.total

integer

Noch nicht bestätigte Einträge insgesamt.

data

Item[]

Charges und Receipts.

Item

Feld

Typ

Beschreibung

chargeType

`"Charge"

"Receipt"`

Siehe Tabelle oben.

bookDate

ISO 8601

Buchungszeitpunkt im POS.

receiptNr

integer

POS-Belegnummer.

userId

integer

Bediener-ID.

userName

string

Bedienername.

outlet

integer

Outlet-ID (1 = Bar, 2 = Restaurant …).

tableNr

integer

Tischnummer.

table

string

Tischbezeichnung.

totalAmount

decimal

Bruttosumme der Zeile.

tip

decimal

Enthaltenes Trinkgeld.

businesscaseId

GUID

APRO-Transaktions-ID — genau dieser Wert wandert als transactionIds in den Confirm-Aufruf.

reservationId

string | null

Reservierungs-ID (bei Charge).

guestId

string | null

Gast-ID.

roomNumber

string | null

Zimmernummer.

charges

LineItem[]

Einzelpositionen.

payments

Payment[]

Nur bei Receipt.

LineItem

Feld

Typ

Beschreibung

articleNr

integer

Artikelnummer.

articleName

string

Artikelname.

productgroupNr

integer

Sparten-Nummer.

productgroupName

string

Sparten-Bezeichnung.

quantity

decimal

Stückzahl.

unitPrice

decimal

Brutto-Einzelpreis.

vatPercent

decimal

Angewendeter Steuersatz.

Beispielantwort

200 OK · Charge + zwei Receipts (gekürzt)

{
  "page": { "limit": 3, "offset": 0, "total": 8096 },
  "data": [
    {
      "chargeType": "Charge",
      "bookDate": "2026-04-15T09:15:04.95",
      "receiptNr": 90,
      "userId": 20,
      "userName": "Johannes",
      "outlet": 1,
      "tableNr": 2,
      "table": "Bar 2",
      "totalAmount": 9.00,
      "tip": 0.30,
      "businesscaseId": "253c225f-7c4a-4d3f-80cd-63de4dbfca9e",
      "reservationId": "   15631",
      "guestId": "    6158",
      "roomNumber": "   414",
      "charges": [
        { "articleNr": 129,  "articleName": "Zipfer 0,2",     "productgroupName": "Bier",        "quantity": 1.00, "unitPrice": 1.10, "vatPercent": 20.00 },
        { "articleNr": 213,  "articleName": "Apfel ltg 0,5",  "productgroupName": "Flaschen AF", "quantity": 1.00, "unitPrice": 2.70, "vatPercent": 20.00 },
        { "articleNr": 1626, "articleName": "Würstel Pommes", "productgroupName": "Hauptspeise", "quantity": 1.00, "unitPrice": 4.90, "vatPercent": 10.00 }
      ]
    }
  ]
}

Vollständiges Beispiel (Charge + zwei Receipts) herunterladen: hotel-charges-response.json

Achtung
String-Felder wie reservationId, guestId oder roomNumber können
links mit Leerzeichen aufgefüllt sein, wenn die Kassa diese Felder
fixer Breite speichert (" 15631"). Im PMS vor dem Matching
trim()en.

3. Charges bestätigen

Bestätigt die erfolgreich verarbeitete Liste. Nach der Bestätigung liefert /charges mit excludeAlreadyExported=true diese businesscaseIds nicht mehr.

POST https://cloudgateway.apro.at/hotelservice/api/v1/pms/confirm-charges

Request-Body

Feld

Typ

Pflicht

Beschreibung

hotelId

string

✅

PMS-Mandant / Hotel.

transactionIds

GUID[]

✅

businesscaseId-Werte aus der vorangegangenen /charges-Antwort.

POST /hotelservice/api/v1/pms/confirm-charges

{
  "hotelId": "1",
  "transactionIds": [
    "69F94B1A-3453-7017-8DDB-94E2175D0365",
    "155FFA87-DC19-EB1F-3DD8-540EF62A115D"
  ]
}

Download: hotel-confirm-charges.json

4. Reservierung entfernen

Explizite Entfernung — sinnvoll beim Check-out oder wenn der Gast nicht mehr aufs Zimmer buchen darf.

DELETE https://cloudgateway.apro.at/hotelservice/api/v1/pms/reservations
    ?hotelId=1
    &reservationId=Abc123
    &guestId=Abc123

Alle drei Query-Parameter sind Pflicht. Die betroffene Reservierung wird entfernt, oder der Call ist ein No-Op, falls sie nicht existiert.

Hinweis
In der Praxis reicht der Voll-Upsert aus Schritt 1 — jede Reservierung,
die nicht mehr in der Payload steht, wird automatisch ausgecheckt
(checkedOutReservationsCount). Diesen Endpunkt nur für gezielte
Out-of-Band-Entfernungen nutzen.

Idempotenz & Retries

  • POST /pms/reservations ist idempotent über { hotelId, Id, guestId }. Gleicher Payload → gleicher Zustand.
  • POST /pms/confirm-charges ist idempotent über transactionIds. Eine erneute Bestätigung derselben ID ist ein No-Op.
  • Poll → Confirm muss in eurem System transaktional sein: zuerst die Charges ins Journal schreiben, dann bestätigen. Schlägt die Bestätigung fehl, liefert der nächste Poll dieselben Zeilen erneut.

Fehler

Siehe Authentifizierung → Fehlerantworten. Bei fachlichen Fehlern liefert die Antwort:

{
  "isSuccessfull": false,
  "errorCode": 42,
  "message": "No reservation found for guestId 'Abc123'.",
  "context": { "guestId": "Abc123" }
}

Gutscheine API

Die Gutschein-API ist die programmatische Schnittstelle zum zentralen APRO-Gutscheinbestand — dieselbe Basis, die Verkaufskarten im Lokal, Online-Gutscheinshops und Treueguthaben speist. Salden sind serverseitig autoritativ; jede Operation liefert den aktualisierten Gutschein zurück, sodass der Aufrufer keinen Zustand lokal tracken muss.

Beträge

Alle Geldbeträge sind Integer in Minor-Units (Cent) — 5000 entspricht € 50,00. balance ist das aktuell verfügbare Guthaben; amount ist der ursprüngliche Nennwert; maxAmount (optional) begrenzt das Aufladelimit.

Voucher-Objekt

Das Voucher-Objekt taucht in jeder Antwort auf. Felder:

Feld

Typ

Beschreibung

id

GUID

Interne APRO-Gutschein-ID (URL-Parameter).

voucherCode

string

Lesbarer Code auf der Karte/dem Beleg.

gift.amount

integer

Ursprünglicher Nennwert (Minor Units).

gift.balance

integer

Verfügbares Guthaben (Minor Units).

gift.maxAmount

integer | null

Aufladelimit; null = unbegrenzt.

codeId

string | null

Optionale Zweit-ID.

startDate

ISO 8601 | null

Vor diesem Zeitpunkt nicht gültig.

expirationDate

ISO 8601 | null

Nach diesem Zeitpunkt nicht gültig.

enabled

bool

false = manuell gesperrt.

comment

string | null

Freitext-Notiz.

categoryId

integer

Gutscheinkategorie (Ausgabekanal).

categoryName

string

Kategoriename.

categoryNrOfTimesRedeemablePerUnit

integer | null

Einlöse-Limit auf Kategorie-Ebene.

categoryExternalPaymentProviderId

string | null

Verweis auf externen Payment-Provider.

createdAt

ISO 8601

Ausgabezeitpunkt.

discriminator

string

"GiftVoucherDto" für Gutschein-Karten.

1. Gutscheine auflisten

GET https://cloudgateway.apro.at/voucherservice/api/v1/vouchers

Liefert alle für den API-Key sichtbaren Gutscheine. Für einzelne Codes den validity-Endpoint nutzen — der ist dafür indiziert.

2. Gültigkeit prüfen

GET https://cloudgateway.apro.at/voucherservice/api/v1/vouchers/validity
    ?voucherCode=560620085

Query-Parameter

Name

Typ

Pflicht

Beschreibung

voucherCode

string

✅

Der auf dem Gutschein aufgedruckte Code.

Beispielantwort

200 OK

{
  "voucher": {
    "gift": { "amount": 5000, "balance": 3880, "maxAmount": null },
    "id": "f5f4d577-3764-4c25-12f8-08db73dad741",
    "voucherCode": "560620085",
    "codeId": null,
    "startDate": null,
    "expirationDate": null,
    "enabled": true,
    "comment": null,
    "categoryId": 4,
    "categoryName": "OnlineGutschein",
    "categoryNrOfTimesRedeemablePerUnit": null,
    "categoryExternalPaymentProviderId": null,
    "createdAt": "2023-06-23T11:13:15.6386753",
    "discriminator": "GiftVoucherDto"
  },
  "errors": [],
  "isValid": true
}

Download: voucher-validity-response.json

isValid: false + befüllter errors-Array sagt, warum der Gutschein gerade nicht einlösbar ist. Häufige Codes:

Fehler

Bedeutung

NotFound

voucherCode existiert nicht.

Expired

expirationDate überschritten.

NotYetStarted

Vor startDate.

Disabled

enabled = false.

NoBalance

balance == 0.

CategoryCapReached

categoryNrOfTimesRedeemablePerUnit erschöpft.

3. Gutschein ausstellen

POST https://cloudgateway.apro.at/voucherservice/api/v1/vouchers/gifts

Legt einen neuen Gutschein an. Bei Erfolg ist der HTTP-Status 201 Created und der Location-Header verweist auf die neue Ressource.

Request-Body

Feld

Typ

Pflicht

Beschreibung

categoryId

integer

✅

Gutscheinkategorie.

voucherCode

string

✅

Aufgedruckter Code. Muss eindeutig sein.

comment

string

—

Freitext-Notiz.

gift.amount

integer

✅

Nennwert in Minor Units.

gift.maxAmount

integer

—

Aufladelimit.

clientType

string

✅

Kennung des aufrufenden Systems. Wird von APRO für deine Integration vergeben — siehe clientType.

clientContext

string (JSON)

—

Serialisierte JSON-Zusatzinfo (Outlet etc.).

startDate

ISO 8601

—

Gültig ab.

expirationDate

ISO 8601

—

Gültig bis.

Beispielanfrage

POST /voucherservice/api/v1/vouchers/gifts

{
  "categoryId": 1,
  "voucherCode": "2131231",
  "comment": "Ersatzkarte — Original verloren",
  "gift": { "amount": 50, "maxAmount": 10000 },
  "clientType": "<von-apro-vergeben>",
  "clientContext": "{\"outletId\":\"231231\"}",
  "startDate": "2026-01-01T00:00:00",
  "expirationDate": "2028-12-31T23:59:59"
}

Download: voucher-issue-request.json

Beispielantwort

201 Created

{
  "data": {
    "gift": { "amount": 50, "balance": 50, "maxAmount": 10000 },
    "id": "e961c17d-d037-4d27-734d-08ddda944e39",
    "voucherCode": "2131231",
    "categoryId": 1,
    "categoryName": "Standard",
    "enabled": true,
    "comment": "Ersatzkarte — Original verloren",
    "startDate": "2026-01-01T00:00:00",
    "expirationDate": "2028-12-31T23:59:59",
    "createdAt": "2026-04-20T21:33:00.000+02:00",
    "discriminator": "GiftVoucherDto"
  }
}

Download: voucher-issue-response.json

4. Gutschein einlösen

POST https://cloudgateway.apro.at/voucherservice/api/v1/vouchers/{voucherId}/redemption

Zieht amount Minor Units vom Guthaben ab. Der Pfadparameter ist die interne id (nicht der voucherCode) — zu erhalten über /validity.

Request-Body

Feld

Typ

Pflicht

Beschreibung

redemption.amount

integer

✅

Abzubuchender Betrag in Minor Units.

clientReference

string

—

Opake Trace-Zeichenkette (z. B. "{ \"waiterId\": 10 }"). Wird persistiert.

clientContext

string (JSON)

—

Zusatzinfo.

comment

string

—

Freitext-Notiz.

clientType

string

✅

Kennung des aufrufenden Systems. Wird von APRO vergeben — siehe clientType.

Beispielanfrage

POST /voucherservice/api/v1/vouchers/{id}/redemption

{
  "redemption": { "amount": 50 },
  "clientReference": "{ \"waiterId\": 10 }",
  "clientContext": "{\"outletId\":\"231231\"}",
  "comment": "Eingelöst an Bar 2 zu Beleg 5906",
  "clientType": "<von-apro-vergeben>"
}

Download: voucher-redeem-request.json

Beispielantwort

200 OK

{
  "data": {
    "redemption": { "amount": 50, "items": [] },
    "success": true,
    "errors": [],
    "transactionId": 21142,
    "voucher": {
      "gift": { "amount": 5000, "balance": 3830, "maxAmount": null },
      "id": "f5f4d577-3764-4c25-12f8-08db73dad741",
      "voucherCode": "560620085",
      "categoryId": 4,
      "categoryName": "OnlineGutschein",
      "enabled": true,
      "discriminator": "GiftVoucherDto"
    }
  }
}

Download: voucher-redeem-response.json

Die transactionId (integer) ist die autoritative ID der Einlösung — für Abgleich und Refunds persistieren.

5. Gutschein aufbuchen

POST https://cloudgateway.apro.at/voucherservice/api/v1/vouchers/{voucherId}/booking

Bucht amount Minor Units zurück auf den Gutschein — etwa beim Aufladen einer Mehrweg-Karte oder bei Rückbuchungen.

Request-Body

Feld

Typ

Pflicht

Beschreibung

amount

integer

✅

Gutzubuchender Betrag in Minor Units. gift.maxAmount beachten.

clientReference

string

—

Opake Trace-Zeichenkette.

clientType

string

✅

Kennung des aufrufenden Systems. Wird von APRO vergeben — siehe clientType.

clientContext

string (JSON)

—

Zusatzinfo.

comment

string

—

Freitext-Notiz.

Beispielanfrage

POST /voucherservice/api/v1/vouchers/{id}/booking

{
  "amount": 1000,
  "clientReference": "{ \"waiterId\": 10 }",
  "clientType": "<von-apro-vergeben>",
  "clientContext": "{\"outletId\":\"231231\"}",
  "comment": "Aufladung nach Guthabenzukauf"
}

Download: voucher-booking-request.json

Beispielantwort

200 OK

{
  "data": {
    "newAmount": 4830,
    "success": true,
    "errors": [],
    "transactionId": 21143,
    "voucher": {
      "gift": { "amount": 5000, "balance": 4830, "maxAmount": null },
      "id": "f5f4d577-3764-4c25-12f8-08db73dad741",
      "voucherCode": "560620085",
      "categoryName": "OnlineGutschein",
      "enabled": true,
      "discriminator": "GiftVoucherDto"
    }
  }
}

Download: voucher-booking-response.json

clientType

clientType identifiziert das aufrufende System, damit der Gutschein-Ledger weiß, welche Integration eine Transaktion erzeugt hat. APRO vergibt für jede Integration beim Onboarding einen eigenen clientType-Wert — der konkrete String ist nicht frei wählbar und wird zusammen mit dem API-Key übermittelt. Immer den von APRO bereitgestellten Wert verwenden, nie selbst erraten.

Fehlerbehandlung

  • HTTP 4xx bei Validierungsfehlern (ungültige voucherId, malformed Body, fehlende Auth). Siehe Authentifizierung → Fehlerantworten.
  • HTTP 200 mit success: false bei fachlichen Regeln (zu wenig Guthaben, abgelaufen …). errors-Array inspizieren.
  • Niemals clientseitig retryen ohne vorherige Validierung. Timeout auf einen Einlösen-Call? Zuerst /validity aufrufen — die Operation kann serverseitig bereits angewendet sein.