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.
- Host:
cloudgateway.apro.at(Pfadpräfix/hotelservice/) - Auth: API-Key im
x-api-key - Content type:
application/json - Ergänzende Referenz: api-docs.apro.at → hotelservice ↗
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/reservationsRequest-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
{ "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
{ "newReservationsCount": 0, "checkedOutReservationsCount": 0, "updatedReservationsCount": 1, "state": 1, "isSuccessfull": true, "errorCode": 0, "message": null, "context": null}Download: hotel-reservations-response.json
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=trueQuery-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”` |
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
{ "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
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-chargesRequest-Body
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
hotelId | string | ✅ | PMS-Mandant / Hotel. |
transactionIds | GUID[] | ✅ | businesscaseId-Werte aus der vorangegangenen /charges-Antwort. |
{ "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=Abc123Alle drei Query-Parameter sind Pflicht. Die betroffene Reservierung wird entfernt, oder der Call ist ein No-Op, falls sie nicht existiert.
Idempotenz & Retries
POST /pms/reservationsist idempotent über{ hotelId, Id, guestId }. Gleicher Payload → gleicher Zustand.POST /pms/confirm-chargesist idempotent übertransactionIds. 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" }}