When I decided to get serious about hospitality technology, I made a list of the things I didn’t understand. At the top: how a Property Management System actually works from the inside.
You can read the HTNG specification. You can browse Mews’s developer docs. But there’s a gap between reading about a system and understanding what it feels like to integrate with one where it fights you, where it surprises you, where the documentation is simply wrong.
So I built a mock. Not a production integration a fake Oracle Opera that emits realistic webhook payloads so I could build against it without needing a real license.
What I expected
I expected the hard part to be the XML parsing. HTNG uses a verbose XML format OTA_HotelResNotifRQ, OTA_HotelResModifyRQ and I anticipated that wrestling with namespaces and deeply nested structures would be the main challenge.
It wasn’t. XML parsing is mechanical. You build a schema, write a parser, write tests. Done in two days.
What was actually hard
The hard part was the domain model.
A reservation in a PMS is not a simple thing. It carries: a rate plan (with its own code, discount rules, and distribution restrictions), a room type (not the same as a room assignment), a market segment code, a guarantee type, multiple guest profiles (booker vs. occupant), a folio structure for charges, and a status lifecycle with at least six states that vary by system.
None of that complexity is apparent from the outside. A hotel guest experiences a booking as a confirmation number. An engineer building on top of a PMS experiences it as 400 fields in a single XML message, most of which interact with each other in underdocumented ways.
The specific things the documentation doesn’t tell you
Three examples that cost me significant time:
1. RatePlanCode is load-bearing. If you omit or mismap the rate plan code in a reservation sync, you don’t get an error. You get a reservation that silently books at the wrong rate. There’s no validation at the message boundary the downstream system just uses whatever’s there.
2. Cancellation events are not reliable. In my mock, I assumed that a cancelled reservation would always emit a cancellation event. In real Opera, cancellations can happen through multiple paths front desk override, channel manager sync, automatic no-show processing and not all of them reliably trigger the same webhook. You need a reconciliation job.
3. “Room type” and “room assignment” are different things. A guest books a room type (Deluxe King). Room assignment (room 412) happens at check-in. Systems that treat these as the same thing break in interesting ways when a room is taken out of inventory between booking and arrival.
What I’d tell someone starting out
Get access to a sandbox as early as possible Mews, Apaleo, and Cloudbeds all have developer environments. Don’t build against mocks for longer than you have to. The mocks teach you the shape of the problem; the real systems teach you the texture.
And read the HTNG specification, yes but treat it as a starting point, not a contract. Every PMS implements a subset of it, and the subsets don’t always match.