Hotel API (PMS)
The Hotel API is a two-way bridge between an APRO point-of-sale and any Property Management System (PMS). The PMS pushes reservations into the POS so that guests can charge to their room; the POS exposes those charges back to the PMS so they land on the final hotel invoice.
- Host:
cloudgateway.apro.at(path prefix/hotelservice/) - Auth: API key in
x-api-key - Content type:
application/json - Upstream reference: api-docs.apro.at → hotelservice ↗
Typical flow
PMS APRO POS │ 1. POST /pms/reservations ─────────▶ │ (upsert guest rooms) │ │ │ │ guests charge to room… │ │ │ 2. GET /pms/charges?hotelId=… ─────▶│ │ ◀─── charges (and optionally receipts) │ │ │ │ 3. POST /pms/confirm-charges ───────▶│ (ack; won't be resent) │ │ │ 4. DELETE /pms/reservations?… ───────▶│ (on check-out / close-out)Step 2 is the PMS’s polling loop. Steps 1 and 4 are driven by events in the PMS. Step 3 must always follow a successful poll — otherwise the same charges will be returned again on the next poll.
1. Upsert reservations
Push the current list of active reservations into the POS. Keys are
hotelId + Id + guestId; existing rows are updated in place.
POST https://cloudgateway.apro.at/hotelservice/api/v1/pms/reservationsRequest body
| Field | Type | Required | Description |
|---|---|---|---|
reservations | Reservation[] | ✅ | One or more reservations. |
Reservation
| Field | Type | Required | Description |
|---|---|---|---|
Id | string | ✅ | PMS-side reservation ID. |
hotelId | string | ✅ | PMS tenant / property ID. |
guestId | string | ✅ | PMS-side guest ID. |
roomNr | string | ✅ | Assigned room number. |
title | string | — | Mr., Mrs., etc. |
firstName | string | — | |
lastName | string | — | |
arrival | ISO 8601 | ✅ | Check-in timestamp. |
departure | ISO 8601 | ✅ | Check-out timestamp. |
nrOfIndividuals | integer | — | Guest count, used for covers / buffet rules. |
identifiers | Identifier[] | — | Keycard UIDs, NFC tags, room tablets etc. — any token the POS may receive when a guest wants to charge to a room. |
Identifier
| Field | Type | Description |
|---|---|---|
value | string | The identifier value (keycard UID, token). |
Example request
{ "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
Example response
{ "newReservationsCount": 0, "checkedOutReservationsCount": 0, "updatedReservationsCount": 1, "state": 1, "isSuccessfull": true, "errorCode": 0, "message": null, "context": null}Download: hotel-reservations-response.json
2. Poll charges
Fetch everything booked against rooms since the last confirmed poll.
GET https://cloudgateway.apro.at/hotelservice/api/v1/pms/charges ?hotelId=1 &excludeAlreadyExported=trueQuery parameters
| Name | Type | Required | Description |
|---|---|---|---|
hotelId | string | ✅ | PMS tenant / property ID. |
excludeAlreadyExported | bool | — | true = only return items not yet confirmed (the usual polling mode). false = re-fetch everything in the configured window. |
Response shape
Each item has a chargeType that controls which fields are populated:
chargeType | Meaning | payments present? |
|---|---|---|
Charge | An open line booked to the room (guest hasn’t paid yet — POS is posting to the room folio). | no |
Receipt | A closed receipt (guest settled at the POS; PMS may ignore or reconcile). | yes |
Top-level envelope:
| Field | Type | Description |
|---|---|---|
page.limit | integer | Page size applied. |
page.offset | integer | Offset into the backlog. |
page.total | integer | Items still unconfirmed in total. |
data | Item[] | Charges and receipts. |
Item
| Field | Type | Description |
|---|---|---|
chargeType | `“Charge" | "Receipt”` |
bookDate | ISO 8601 | When the line was booked in the POS. |
receiptNr | integer | POS receipt number. |
userId | integer | Operator / waiter ID. |
userName | string | Operator name. |
outlet | integer | Outlet ID (e.g. 1 = bar, 2 = restaurant). |
tableNr | integer | Table number. |
table | string | Table label. |
totalAmount | decimal | Gross total of the line. |
tip | decimal | Included tip. |
businesscaseId | GUID | APRO transaction ID — use this as the transactionIds value to confirm. |
reservationId | string | null | PMS reservation ID, for Charge items. |
guestId | string | null | PMS guest ID. |
roomNumber | string | null | Room number. |
charges | LineItem[] | Individual article rows. |
payments | Payment[] | Only on Receipt rows. |
LineItem
| Field | Type | Description |
|---|---|---|
articleNr | integer | Product number. |
articleName | string | Product name. |
productgroupNr | integer | Category (Sparte) number. |
productgroupName | string | Category name. |
quantity | decimal | Quantity. |
unitPrice | decimal | Gross unit price. |
vatPercent | decimal | Applied VAT rate. |
Example response
{ "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 } ] } ]}Download the full sample (Charge + two Receipts):
hotel-charges-response.json
3. Confirm charges
Acknowledge the list you’ve successfully ingested. Once confirmed, the
POS won’t return those businesscaseIds on a subsequent poll with
excludeAlreadyExported=true.
POST https://cloudgateway.apro.at/hotelservice/api/v1/pms/confirm-chargesRequest body
| Field | Type | Required | Description |
|---|---|---|---|
hotelId | string | ✅ | PMS tenant / property ID. |
transactionIds | GUID[] | ✅ | businesscaseId values from the previous /charges response. |
{ "hotelId": "1", "transactionIds": [ "69F94B1A-3453-7017-8DDB-94E2175D0365", "155FFA87-DC19-EB1F-3DD8-540EF62A115D" ]}Download: hotel-confirm-charges.json
4. Remove a reservation
Explicit removal — useful at check-out or when the guest should no longer be able to charge to the room.
DELETE https://cloudgateway.apro.at/hotelservice/api/v1/pms/reservations ?hotelId=1 &reservationId=Abc123 &guestId=Abc123All three query parameters are required. Either the targeted reservation is removed, or the call is a no-op if it doesn’t exist.
Idempotency & retries
POST /pms/reservationsis idempotent on{ hotelId, Id, guestId }. Re-sending the same payload converges to the same state.POST /pms/confirm-chargesis idempotent ontransactionIds. Re-confirming an already-confirmed ID is a no-op.- Poll → confirm must be transactional in your own system: write the charges to your ledger first, then confirm. If the confirm fails, the same rows are returned next poll.
Errors
See Authentication → Error responses. On business-logic failures the response shape is:
{ "isSuccessfull": false, "errorCode": 42, "message": "No reservation found for guestId 'Abc123'.", "context": { "guestId": "Abc123" }}