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[] | ✅ | The reservations to upsert. May be empty together with deleteOthers: true (checks out every reservation). |
deleteOthers | bool | — | Default false. true = every reservation the POS holds for this hotel that is not in reservations is checked out. Without this flag nothing is ever checked out by this call. |
hotelId | string | (✅) | PMS tenant / property ID. Required when reservations is empty; otherwise the hotelId of the first reservation is used. |
Reservation
| Field | Type | Required | Max. length | Description |
|---|---|---|---|---|
Id | string | ✅ | 40 | PMS-side reservation ID. |
hotelId | string | ✅ | — | PMS tenant / property ID. |
guestId | string | ✅ | 8 | PMS-side guest ID. |
roomNr | string | ✅ | 15 | Assigned room number. |
title | string | — | 20 | Mr., Mrs., etc. |
firstName | string | — | 100 | |
lastName | string | — | 100 | Shown at the POS with at most 40 characters. |
arrival | ISO 8601 | ✅ | Check-in timestamp. | |
departure | ISO 8601 | ✅ | Check-out timestamp. | |
nrOfIndividuals | integer | — | Guest count, used for covers / buffet rules. | |
address | Address | — | Postal address. | |
contact | Contact | — | Contact data. | |
additionalInfo | string | — | 1000 | Free text shown with the guest. |
additionalInfoJson | string | — | 1000 | Free key/value data as a JSON string. |
identifiers | Identifier[] | — | Keycard UIDs, NFC tags, room tablets etc. — any token the POS may receive when a guest wants to charge to a room. |
Address
| Field | Type | Max. length |
|---|---|---|
street | string | 100 |
postalCode | string | 50 |
city | string | 100 |
country | string | 50 |
Contact
| Field | Type | Max. length |
|---|---|---|
salutation | string | 50 (not truncated, see below) |
phoneNumber | string | 20 |
mobilePhoneNumber | string | 20 |
faxNumber | string | 20 |
mailAddress | string | 100 |
birthday | ISO 8601 |
Identifier
| Field | Type | Max. length | Description |
|---|---|---|---|
value | string | 40 | The identifier value (keycard UID, token). Only the first identifier is used. |
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
Checking out via the list (deleteOthers)
Reservations are only checked out by this call when deleteOthers is
true. Then every reservation the POS holds for the hotel that is
not in the payload is removed, and checkedOutReservationsCount
reports how many. To check out the last remaining guests, send an
empty list:
{ "hotelId": "1", "deleteOthers": true, "reservations": []}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 business case ID. |
transactionId | GUID | APRO transaction ID — send this value in transactionIds 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", "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 } ] } ]}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 transactions 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[] | ✅ | transactionId 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" }}