APRO Dokumentation · Stand 6. November 2026

Hotel API (PMS)

Zuletzt aktualisiert: 5. November 2026ca. 4 Min. Lesezeit

PDF erzeugen

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