Hotel Reservation

The stay card for a booked hotel, for use during the trip, with check-in times, the room, digital key, Wi-Fi and what's due.

Experience · Shares patterns with Hotel Booking, Flight Ticket

≈ 1,628 tokens per use (tool 1,156 · guidance 332 · typical payload 140)How token costs are estimated

Example: Hotel Reservation

My hotel for tonight

Casa Azulejo Lisboa

Confirmation numberCAZ-48213

Tonight · Checked in online

Check-in
15:00 hotel time
Thu, Oct 8
Length of stay
3 nights
Check-out
11:00 hotel time
Sun, Oct 11

Room type: Deluxe King

Room
512
Floor
5
Wi-Fi
CasaAzulejo-Guest
Password
Hidden

When to use

  • The user has a hotel booked and asks about it before or during the trip: "my hotel for tonight", "what time is check-in?", "how do I get into my room?", "what's the Wi-Fi?".
  • They want to check in online, open their digital key, call the hotel or get directions.

When not to use

  • The user is still choosing or booking a hotel. Use Hotel Booking, which also has a confirmation mode right after booking (price paid, cancel).
  • The agent doesn't have the reservation from a tool result. Never invent confirmation numbers, rooms, Wi-Fi details or balances.

How the agent should use it

  • Pass stay dates as calendar dates and check-in/out times as the hotel prints them, with the hotel's IANA timeZone. Never convert them to the user's timezone.
  • Pass room.number only when the hotel has assigned one. Without it the card says "Room assigned at check-in".
  • Pass the Wi-Fi password only from the hotel's own data. It's masked until the user taps Show, and it never comes back in a result. Don't repeat it in the reply unless the user asks.
  • Every action is a handoff. On check-in or open-digital-key, open the hotel's flow; on request-late-checkout, ask the hotel and report its answer. Never say late check-out is granted until the hotel confirms.
  • Lifecycle: set expiresAt on the call to the check-out moment (the check-out date at checkOutUntil, hotel time; checkOutInstant(stay, hotel) in the utils computes it), so the Renderer turns the card inert once the stay is over. Use supersede when the hotel updates the booking (a room is assigned, check-in is done) and the agent shows the card again. fetchedAt isn't needed.

Stay state and times

The header reads the stay against today's date in the hotel's timezone, so "Tonight" is true where the hotel is:

Today, hotel timeState line
Before check-in day"In 3 days", "Tomorrow"
Check-in day"Tonight"
During the stay"Night 2 of 3"
Check-out day, before checkOutUntil"Checking out today"
After that"Stay ended" (check-in, key and late check-out are hidden)

Before the stay starts, "Checked in online" follows the state once online check-in is done.

The state is computed after mount, so server and client render the same markup, and it refreshes every minute. Pass now to measure from a fixed moment instead (tests, demos). Stay dates never shift with the viewer's timezone or across DST; times show on a 24-hour clock, as hotels print them.

Layout

The card reads like a pass: the time that matters and the room number, large.

  • Header: the hotel, the confirmation number with a copy button, and the state line.
  • Times: check-in and check-out times, large, with their dates beneath and the number of nights between them.
  • Room: the room type, then the room number and floor in large type once assigned ("Room assigned at check-in" before), and what's due at the hotel.
  • Wi-Fi: one row with the network and a masked password with Show (a toggle, aria-pressed) and Copy. Copying works while masked.
  • One quiet line for what changes a decision: free cancellation (only before the stay starts), or "No digital key · collect at the front desk".

The address powers Directions and the calendar file; the hotel's website goes into the calendar file only.

Actions

ButtonWhat happens
Check in online{ action: "check-in" }. With checkIn.url, also opens the hotel's page in a new tab
Open digital key{ action: "open-digital-key" }. With digitalKey.url, also opens the hotel's app link
Request late check-out{ action: "request-late-checkout" }. Primary on check-out day
DirectionsAn OpenStreetMap search for the address, in a new tab. Nothing is sent
Call hotelA tel: link. Nothing is sent
Add to calendarDownloads an .ics file for the stay (check-in to check-out, hotel time). Nothing is sent

The footer shows the first two that apply (late check-out first on check-out day, then check-in, key, directions, call, calendar) and puts the rest in a "More actions" menu. Each handoff is reported once and the footer confirms it ("Opening your digital key…"); the others stay available. Without a link a handoff button locks after use; with a link it stays usable, since a key is opened more than once.

Installation

npx shadcn@latest add https://your-site.com/r/hotel-reservation.json

Usage

app/api/chat/route.ts
import { tool } from "ai"
import { hotelReservationTool } from "@/components/genui/hotel-reservation.schema"

const tools = {
  // No execute: the user's first action is the tool result.
  [hotelReservationTool.name]: tool({
    description: hotelReservationTool.description,
    inputSchema: hotelReservationTool.inputSchema,
  }),
}

Render the tool call and hand each action to your own flow:

components/message.tsx
import { HotelReservation } from "@/components/genui/hotel-reservation"

function ToolPart({ part, addToolResult }) {
  if (part.type !== "tool-show_hotel_reservation") return null

  return (
    <HotelReservation
      {...part.input}
      status={part.state === "input-streaming" ? "streaming" : undefined}
      result={part.state === "output-available" ? part.output : undefined}
      onResult={(result) => {
        if (part.state !== "output-available") {
          addToolResult({ tool: "show_hotel_reservation", toolCallId: part.toolCallId, output: result })
        } else {
          sendMessage({ text: `Hotel reservation action: ${JSON.stringify(result)}` })
        }
      }}
    />
  )
}

The agent receives one of:

{ action: "check-in" }
{ action: "open-digital-key" }
{ action: "request-late-checkout" }

With LangGraph

Push it from a graph node with ui.push({ name: "show_hotel_reservation", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.

Restoring from history

When a conversation is reloaded, pass the stored result back as result, or an array of results when several actions were taken. Those actions show as done; they never call onResult again.

<HotelReservation
  {...part.input}
  result={part.state === "output-available" ? part.output : undefined}
/>

Schema

PropTypeNotes
hotel{ name, address, phone?, timeZone, checkInFrom, checkOutUntil, url? }Times as 24-hour "15:00"; IANA timezone; url http(s) only. Data-bound
confirmationstring3–24 letters, digits or dashes. Copy button included. Data-bound
stay{ checkIn, checkOut }Calendar dates, check-out after check-in. Data-bound
room{ type, number?, floor? }?number only once assigned. Data-bound
digitalKey{ available, url? }?url opens the hotel's app; http(s) only. Data-bound
checkIn{ online, done?, url? }?Online check-in. Data-bound
wifi{ network, password? }?Password masked by default; never in a result. Data-bound
balance{ due, currency }?Due at the hotel, e.g. city tax. Data-bound
cancellation{ freeUntil? }?ISO 8601 with offset. Shown before the stay starts. Data-bound

Host props: status (streaming, ready, submitting, done), now, onResult, result and className.

Source

On this page