Skip to content

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.

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/reservations

Request body

FieldTypeRequiredDescription
reservationsReservation[]One or more reservations.

Reservation

FieldTypeRequiredDescription
IdstringPMS-side reservation ID.
hotelIdstringPMS tenant / property ID.
guestIdstringPMS-side guest ID.
roomNrstringAssigned room number.
titlestringMr., Mrs., etc.
firstNamestring
lastNamestring
arrivalISO 8601Check-in timestamp.
departureISO 8601Check-out timestamp.
nrOfIndividualsintegerGuest count, used for covers / buffet rules.
identifiersIdentifier[]Keycard UIDs, NFC tags, room tablets etc. — any token the POS may receive when a guest wants to charge to a room.

Identifier

FieldTypeDescription
valuestringThe identifier value (keycard UID, token).

Example request

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

Example response

200 OK
{
"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=true

Query parameters

NameTypeRequiredDescription
hotelIdstringPMS tenant / property ID.
excludeAlreadyExportedbooltrue = 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:

chargeTypeMeaningpayments present?
ChargeAn open line booked to the room (guest hasn’t paid yet — POS is posting to the room folio).no
ReceiptA closed receipt (guest settled at the POS; PMS may ignore or reconcile).yes

Top-level envelope:

FieldTypeDescription
page.limitintegerPage size applied.
page.offsetintegerOffset into the backlog.
page.totalintegerItems still unconfirmed in total.
dataItem[]Charges and receipts.

Item

FieldTypeDescription
chargeType`“Charge""Receipt”`
bookDateISO 8601When the line was booked in the POS.
receiptNrintegerPOS receipt number.
userIdintegerOperator / waiter ID.
userNamestringOperator name.
outletintegerOutlet ID (e.g. 1 = bar, 2 = restaurant).
tableNrintegerTable number.
tablestringTable label.
totalAmountdecimalGross total of the line.
tipdecimalIncluded tip.
businesscaseIdGUIDAPRO transaction ID — use this as the transactionIds value to confirm.
reservationIdstring | nullPMS reservation ID, for Charge items.
guestIdstring | nullPMS guest ID.
roomNumberstring | nullRoom number.
chargesLineItem[]Individual article rows.
paymentsPayment[]Only on Receipt rows.

LineItem

FieldTypeDescription
articleNrintegerProduct number.
articleNamestringProduct name.
productgroupNrintegerCategory (Sparte) number.
productgroupNamestringCategory name.
quantitydecimalQuantity.
unitPricedecimalGross unit price.
vatPercentdecimalApplied VAT rate.

Example response

200 OK · charge + two receipts
{
"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-charges

Request body

FieldTypeRequiredDescription
hotelIdstringPMS tenant / property ID.
transactionIdsGUID[]businesscaseId values from the previous /charges response.
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. 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=Abc123

All 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/reservations is idempotent on { hotelId, Id, guestId }. Re-sending the same payload converges to the same state.
  • POST /pms/confirm-charges is idempotent on transactionIds. 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" }
}