Zum Inhalt springen

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

FeldTypPflichtBeschreibung
reservationsReservation[]✅Die zu übertragenden Reservierungen. Darf zusammen mit deleteOthers: true leer sein (checkt alle Reservierungen aus).
deleteOthersbool—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.
hotelIdstring(✅)PMS-Mandant / Hotel. Pflicht, wenn reservations leer ist; sonst gilt die hotelId der ersten Reservierung.

Reservation

FeldTypPflichtMax. LängeBeschreibung
Idstring✅40Reservierungs-ID im PMS.
hotelIdstring✅—PMS-Mandant / Hotel.
guestIdstring✅8Gast-ID im PMS.
roomNrstring✅15Zugewiesene Zimmernummer.
titlestring—20Anrede (Mr., Mrs. …).
firstNamestring—100
lastNamestring—100An der Kassa mit höchstens 40 Zeichen angezeigt.
arrivalISO 8601✅Check-in-Zeitpunkt.
departureISO 8601✅Check-out-Zeitpunkt.
nrOfIndividualsinteger—Personenzahl — für Covers/Buffet-Regeln.
addressAddress—Postanschrift.
contactContact—Kontaktdaten.
additionalInfostring—1000Freitext, der beim Gast angezeigt wird.
additionalInfoJsonstring—1000Freie Schlüssel/Wert-Daten als JSON-String.
identifiersIdentifier[]—Keycard-UIDs, NFC-Tokens, Zimmer-Tablets — alle Kennungen, mit denen das POS eine Zimmerbuchung erkennen soll.

Address

FeldTypMax. Länge
streetstring100
postalCodestring50
citystring100
countrystring50

Contact

FeldTypMax. Länge
salutationstring50 (wird nicht gekürzt, siehe unten)
phoneNumberstring20
mobilePhoneNumberstring20
faxNumberstring20
mailAddressstring100
birthdayISO 8601

Identifier

FeldTypMax. LängeBeschreibung
valuestring40Wert der Kennung (Keycard-UID, Token). Nur die erste Kennung wird verwendet.

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

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:

POST /hotelservice/api/v1/pms/reservations · alle auschecken
{
"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=true

Query-Parameter

NameTypPflichtBeschreibung
hotelIdstring✅PMS-Mandant / Hotel.
excludeAlreadyExportedbool—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:

chargeTypeBedeutungpayments vorhanden?
ChargeOffene Buchung aufs Zimmer (Gast hat noch nicht gezahlt — POS bucht aufs Zimmer-Folio).nein
ReceiptGeschlossener Beleg (Gast hat an der Kassa bezahlt; PMS kann ignorieren oder abgleichen).ja

Top-Level:

FeldTypBeschreibung
page.limitintegerAngewandte Seitengröße.
page.offsetintegerOffset im Backlog.
page.totalintegerNoch nicht bestätigte Einträge insgesamt.
dataItem[]Charges und Receipts.

Item

FeldTypBeschreibung
chargeType`“Charge""Receipt”`
bookDateISO 8601Buchungszeitpunkt im POS.
receiptNrintegerPOS-Belegnummer.
userIdintegerBediener-ID.
userNamestringBedienername.
outletintegerOutlet-ID (1 = Bar, 2 = Restaurant …).
tableNrintegerTischnummer.
tablestringTischbezeichnung.
totalAmountdecimalBruttosumme der Zeile.
tipdecimalEnthaltenes Trinkgeld.
businesscaseIdGUIDAPRO-Geschäftsfall-ID.
transactionIdGUIDAPRO-Transaktions-ID — genau dieser Wert wandert als transactionIds in den Confirm-Aufruf.
reservationIdstring | nullReservierungs-ID (bei Charge).
guestIdstring | nullGast-ID.
roomNumberstring | nullZimmernummer.
chargesLineItem[]Einzelpositionen.
paymentsPayment[]Nur bei Receipt.

LineItem

FeldTypBeschreibung
articleNrintegerArtikelnummer.
articleNamestringArtikelname.
productgroupNrintegerSparten-Nummer.
productgroupNamestringSparten-Bezeichnung.
quantitydecimalStückzahl.
unitPricedecimalBrutto-Einzelpreis.
vatPercentdecimalAngewendeter 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",
"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-charges

Request-Body

FeldTypPflichtBeschreibung
hotelIdstring✅PMS-Mandant / Hotel.
transactionIdsGUID[]✅transactionId-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.

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" }
}