Implementation guide
This page is for the engineer wiring roomID into a hotel website, a booking engine, or a PMS export. Each block below is complete: paste it into the page head, replace the example values with your own, and you are done. Identifiers come from the registrar; see the registry policy if your property does not have them yet.
The pattern in all four blocks is the same one: identifier is a schema.org PropertyValue whose propertyID is roomID for a unit or roomID:property for a property, and whose value is the identifier itself.
A room page
Put this on the page that describes one physical room. The sameAs link to the resolver lets any consumer of your markup verify the identifier against the registry.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HotelRoom",
"name": "Room 1412",
"url": "https://www.example-hotel.com/rooms/1412",
"identifier": {
"@type": "PropertyValue",
"propertyID": "roomID",
"value": "7QM4KX92-1412-B"
},
"containedInPlace": {
"@type": "Hotel",
"name": "Example Hotel",
"identifier": {
"@type": "PropertyValue",
"propertyID": "roomID:property",
"value": "7QM4KX92"
}
},
"sameAs": "https://openroomid.org/7QM4KX92-1412-B"
}
</script>A hotel page
Put this on the property's main page. Listing units in containsPlace is optional; the property identifier is the part that matters. Two units are shown as examples of the shape.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Hotel",
"name": "Example Hotel",
"url": "https://www.example-hotel.com",
"identifier": {
"@type": "PropertyValue",
"propertyID": "roomID:property",
"value": "7QM4KX92"
},
"sameAs": "https://openroomid.org/7QM4KX92",
"containsPlace": [
{
"@type": "HotelRoom",
"name": "Room 1412",
"identifier": {
"@type": "PropertyValue",
"propertyID": "roomID",
"value": "7QM4KX92-1412-B"
}
},
{
"@type": "HotelRoom",
"name": "Room 1414",
"identifier": {
"@type": "PropertyValue",
"propertyID": "roomID",
"value": "7QM4KX92-1414-A"
}
}
]
}
</script>An offer attached to a unit
A rate belongs to your markup and your systems, never to the registry. This block shows the join: the Offer carries your price, and itemOffered.identifier pins it to the exact unit, so a consumer of the offer knows precisely which room the price is for.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Offer",
"itemOffered": {
"@type": "HotelRoom",
"name": "Room 1412",
"identifier": {
"@type": "PropertyValue",
"propertyID": "roomID",
"value": "7QM4KX92-1412-B"
}
},
"price": "189.00",
"priceCurrency": "USD",
"availabilityStarts": "2026-09-12",
"availabilityEnds": "2026-09-13",
"seller": {
"@type": "Hotel",
"name": "Example Hotel",
"identifier": {
"@type": "PropertyValue",
"propertyID": "roomID:property",
"value": "7QM4KX92"
}
}
}
</script>Carrying the identifier into booking
The potentialAction pattern advertises a booking URL that carries the identifier as a rid query parameter. A system that selected a specific unit upstream can hand the selection to your booking flow without losing it. The {checkin_date} and {checkout_date} placeholders follow the EntryPoint template convention.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HotelRoom",
"name": "Room 1412",
"identifier": {
"@type": "PropertyValue",
"propertyID": "roomID",
"value": "7QM4KX92-1412-B"
},
"potentialAction": {
"@type": "ReserveAction",
"target": {
"@type": "EntryPoint",
"urlTemplate": "https://book.example-hotel.com/reserve?rid=7QM4KX92-1412-B&checkin={checkin_date}&checkout={checkout_date}",
"actionPlatform": "https://schema.org/DesktopWebPlatform"
},
"result": {
"@type": "LodgingReservation",
"name": "Reservation for room 1412"
}
}
}
</script>Rules worth knowing
- Emit identifiers in canonical form: uppercase property code, designator cased as registered, uppercase suffix.
- Do not mint identifiers yourself. If an identifier does not resolve at openroomid.org, it is not a roomID.
- When a room is renumbered, the registry supersedes the old identifier and redirects to the new one, so stored identifiers in your systems keep working. Update your markup to the current identifier when convenient.
- Validate syntax before storing: section 3 of the specification has the grammar, and the resolver returns 400 with the failing rule for anything malformed.