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[] | ✅ | Die zu übertragenden Reservierungen. Darf zusammen mit deleteOthers: true leer sein (checkt alle Reservierungen aus). |
deleteOthers | bool | — | Standard false. true = jede Reservierung, die die Kassa für dieses Hotel kennt und die nicht in reservations steht, wird ausgecheckt. Ohne dieses Flag checkt dieser Aufruf nie etwas aus. |
hotelId | string | (✅) | PMS-Mandant / Hotel. Pflicht, wenn reservations leer ist; sonst gilt die hotelId der ersten Reservierung. |
Reservation
| Feld | Typ | Pflicht | Max. Länge | Beschreibung |
|---|---|---|---|---|
Id | string | ✅ | 40 | Reservierungs-ID im PMS. |
hotelId | string | ✅ | — | PMS-Mandant / Hotel. |
guestId | string | ✅ | 8 | Gast-ID im PMS. |
roomNr | string | ✅ | 15 | Zugewiesene Zimmernummer. |
title | string | — | 20 | Anrede (Mr., Mrs. …). |
firstName | string | — | 100 | |
lastName | string | — | 100 | An der Kassa mit höchstens 40 Zeichen angezeigt. |
arrival | ISO 8601 | ✅ | Check-in-Zeitpunkt. | |
departure | ISO 8601 | ✅ | Check-out-Zeitpunkt. | |
nrOfIndividuals | integer | — | Personenzahl — für Covers/Buffet-Regeln. | |
address | Address | — | Postanschrift. | |
contact | Contact | — | Kontaktdaten. | |
additionalInfo | string | — | 1000 | Freitext, der beim Gast angezeigt wird. |
additionalInfoJson | string | — | 1000 | Freie Schlüssel/Wert-Daten als JSON-String. |
identifiers | Identifier[] | — | Keycard-UIDs, NFC-Tokens, Zimmer-Tablets — alle Kennungen, mit denen das POS eine Zimmerbuchung erkennen soll. |
Address
| Feld | Typ | Max. Länge |
|---|---|---|
street | string | 100 |
postalCode | string | 50 |
city | string | 100 |
country | string | 50 |
Contact
| Feld | Typ | Max. Länge |
|---|---|---|
salutation | string | 50 (wird nicht gekürzt, siehe unten) |
phoneNumber | string | 20 |
mobilePhoneNumber | string | 20 |
faxNumber | string | 20 |
mailAddress | string | 100 |
birthday | ISO 8601 |
Identifier
| Feld | Typ | Max. Länge | Beschreibung |
|---|---|---|---|
value | string | 40 | Wert der Kennung (Keycard-UID, Token). Nur die erste Kennung wird verwendet. |
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
Auschecken über die Liste (deleteOthers)
Dieser Aufruf checkt nur dann Reservierungen aus, wenn deleteOthers
true ist. Dann wird jede Reservierung des Hotels entfernt, die die
Kassa kennt und die nicht in der Payload steht;
checkedOutReservationsCount meldet die Anzahl. Um die letzten
verbliebenen Gäste auszuchecken, eine leere Liste senden:
{ "hotelId": "1", "deleteOthers": true, "reservations": []}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-Geschäftsfall-ID. |
transactionId | 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", "transactionId": "69f94b1a-3453-7017-8ddb-94e2175d0365", "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
Transaktionen 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[] | ✅ | transactionId-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" }}