1Stay API
Reference
1Stay gives AI agents and applications access to real hotel inventory — 300,000+ properties across 140+ countries — through a simple MCP server. Reservations use the hotel's own confirmation number. Loyalty points are eligible. You're never the merchant of record.
The MCP server name is 1stay at endpoint mcp.stayker.com. There are 8 tools covering the complete booking lifecycle.
MCP Server Details
// Claude Desktop / claude_desktop_config.json { "mcpServers": { "1stay": { "url": "https://mcp.stayker.com/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } }
Authorization: Bearer YOUR_API_KEY
What Makes This Different
Most hotel APIs are OTA-style: you buy inventory, mark it up, you're the merchant. 1Stay is infrastructure: your agent connects guests to hotels directly. The hotel charges the guest. You build the experience, your users pay for your product — 1Stay is the booking layer that makes it possible. No inventory risk, no float, no chargebacks.
Quick
Start
From zero to a hotel checkout link in three tool calls. The canonical flow for any AI agent.
Search for hotels
Call search_hotels with location (or lat/lng coordinates), dates, and guest count. Returns a list of available properties with rates. Note the parameter is guests_per_room, not guests.
// Tool: search_hotels { "location": "Austin, TX", "check_in": "2027-01-15", "check_out": "2027-01-18", "guests_per_room": 2 } // Returns: list of hotels with hotel_id, rates, rate_code per rate
Get hotel details
Call get_hotel_details with the hotel_id and the dates — both are required, since rates cannot be priced without them. Returns room types, rates, cancellation policies, amenities, and everything your agent needs.
// Tool: get_hotel_details { "hotel_id": "htl_x9y8z7", "check_in": "2027-01-15", "check_out": "2027-01-18", "guests": 2 } // Returns: rates[], room types, cancellation policies, amenities
Book it
Call book_hotel with the selected rate_code. It accepts no guest identity or payment fields and returns secure checkout, where the guest supplies them.
// Tool: book_hotel { "hotel_id": "htl_x9y8z7", "rate_code": "RAC-STK-F8E2", "check_in": "2027-01-15", "check_out": "2027-01-18", "guests": 2 } // Returns: checkout_url, total — guest pays at checkout URL
API Keys &
Auth
All requests require a Bearer token in the Authorization header. API keys are issued per application and scoped to either sandbox or production mode.
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Sandbox keys begin with sk_test_. Production keys begin with sk_live_. You'll receive your key after approval and Stripe onboarding.
Key Scopes
Production plans include 50,000 searches per month with overage billed at $0.002/search. Sandbox keys use test properties and never charge guests. Production keys access live inventory and create real reservations.
search_hotels
Search for exactly one room by location and dates. Returns a ranked list of properties with rates and a search_id valid for 15 minutes. The first 1Stay release supports one room per search and reservation.
| Parameter | Type | Required | Description |
|---|---|---|---|
| location | string | optional | City, address, venue, or landmark. e.g. "Nashville, TN" or "Times Square, NYC". Required unless latitude and longitude are both provided. |
| check_in | string | required | Check-in date. ISO 8601 format: YYYY-MM-DD. Must be today or later. |
| check_out | string | required | Check-out date. ISO 8601 format: YYYY-MM-DD. Must be after check_in. |
| guests_per_room | integer | optional | Guests per room. Default: 2. |
| rooms | integer | optional | Must be 1. Omit this field or set it to 1; multi-room searches are not supported in the first release. |
| latitude | number | optional | Latitude — must be provided together with longitude. |
| longitude | number | optional | Longitude — must be provided together with latitude. |
| radius | integer | optional | Search radius in miles. Default: 25. Max: 100. |
| currency | string | optional | ISO 4217 currency code for rates. Default: USD. |
| max_results | integer | optional | Max hotels to return. Default: 4. Max: 15. |
| search_id | string | optional | Search ID from previous results — pass with cursor to fetch the next page. |
| cursor | string | optional | Pagination cursor from a previous search response. |
{
"location": "Nashville, TN",
"check_in": "2026-06-12",
"check_out": "2026-06-15",
"guests_per_room": 2
}
{
"search_id": "srch_a1b2c3d4e5f6",
"total_results": 4,
"cursor": "next-page-token",
"has_more": true,
"results": [
{
"hotel_id": "htl_x9y8z7w6",
"name": "The Hermitage Hotel",
"star_rating": 5,
"total_stay": 654.37,
"avg_nightly_rate": 189.00,
"booking_fee": 7.00,
"currency": "USD",
"reward_points_eligible": true
}
]
}
get_hotel_details
Get room types, live rates, amenities, and cancellation policies for a specific hotel. This is the tool your agent calls before booking — it returns rate_code values needed by book_hotel. Rate codes expire in ~15 minutes.
| Parameter | Type | Required | Description |
|---|---|---|---|
| hotel_id | string | required | Hotel ID from a search_hotels response |
| check_in | string | required | Check-in date (YYYY-MM-DD) |
| check_out | string | required | Check-out date (YYYY-MM-DD) |
| guests | integer | optional | Number of guests (default 2) |
| rooms | integer | optional | Must be 1. Hotel details price exactly one room in the first release. |
| accessible | boolean | optional | Return accessible (ADA) room types instead of standard ones. Omitted by default because hotels list many near-identical accessible variants. The response always reports accessible_rooms_available, so call again with accessible: true when a guest needs one. |
{
"hotel_id": "TP-MC-DALCC",
"check_in": "2026-06-12",
"check_out": "2026-06-15",
"guests": 2
}
{
"hotel": {
"hotel_id": "TP-MC-DALCC",
"name": "Dallas Marriott City Center",
"chain": "Marriott",
"check_in_time": "15:00",
"check_out_time": "12:00"
},
"rooms": [
{
"room_id": "rm_01",
"name": "King Guest Room",
"description": "One king bed, city view",
"bed_type": "King",
"max_occupancy": 2,
"rate_plans": [
{
"rate_code": "RAC-STK-A1B2",
"name": "Best Available Rate",
"avg_nightly_rate": 189.00,
"subtotal": 572.00,
"taxes": 82.37,
"total_with_taxes": 654.37,
"currency": "USD",
"nightly_breakdown": [
{ "date": "2026-06-12", "amount": 189.00 },
{ "date": "2026-06-13", "amount": 189.00 },
{ "date": "2026-06-14", "amount": 189.00 }
],
"refundable": true,
"cancellation_policy": "Free cancellation until 48hrs before arrival",
"reward_points_eligible": true,
"booking_type": "Direct hotel confirmation"
}
]
}
],
"rate_quote_expires_at": "2026-06-10T14:45:00Z",
"rate_quote_ttl_seconds": 900
}
book_hotel with the rate_code before it expires. If expired, call get_hotel_details again for fresh rates.
book_hotel
Start secure checkout for exactly one room using a rate_code from get_hotel_details. The tool accepts no guest identity or payment fields. A reservation is created only after the guest completes checkout.
| Parameter | Type | Required | Description |
|---|---|---|---|
| hotel_id | string | required | Hotel ID from a search_hotels or get_hotel_details response |
| rate_code | string | required | Rate code from get_hotel_details response |
| check_in | string | required | Check-in date. ISO 8601 format: YYYY-MM-DD |
| check_out | string | required | Check-out date. ISO 8601 format: YYYY-MM-DD |
| guests | integer | required | Number of adult guests |
| external_reference_id | string | optional | Your own reference ID for this booking. Stored for your records. |
checkout_url returned by this tool is where the guest supplies identity and payment details. The reservation is not confirmed until checkout completes. Checkout and its live rate expire in approximately 15 minutes.
{
"hotel_id": "htl_x9y8z7w6",
"rate_code": "RAC-STK-F8E2",
"check_in": "2026-06-12",
"check_out": "2026-06-15",
"guests": 2
}
{
"status": "ACTION_REQUIRED",
"message": "Complete the reservation on the secure checkout page.",
"checkout_url": "https://book.1stay.ai/checkout/…",
"total_with_taxes": 567.00,
"booking_fee": 7.00,
"currency": "USD",
"expires_in_minutes": 15
}
get_booking
Retrieve an existing reservation exclusively by the hotel's confirmation number. Anonymous callers also pass the booking-scoped verification token returned by lookup_booking. Internal Stayker booking IDs are never accepted or disclosed through MCP.
| Parameter | Type | Required | Description |
|---|---|---|---|
| confirmation_number | string | required | Hotel confirmation number shown in the guest's confirmation email |
| verification_token | string | optional | Not needed when authenticated with your own API key — your access is already scoped to your own bookings. The parameter exists for authless guest-facing callers, which must obtain a token from lookup_booking first; it does not apply to your integration. |
{
"status": "confirmed",
"confirmation_number": "HLC-98234",
"hotel": "The Hermitage Hotel",
"check_in": "2026-06-12",
"check_out": "2026-06-15",
"guest_name": "Jane Smith",
"total": 567.00,
"created_at": "2026-06-10T14:52:33Z"
}
lookup_booking
Look up a reservation with identity verification. The guest must provide their name plus at least one verification factor. Returns a booking summary with status, dates, and hotel information.
| Parameter | Type | Required | Description |
|---|---|---|---|
| first_name | string | required | Guest first name on the reservation |
| last_name | string | required | Guest last name on the reservation |
| confirmation_number | string | optional* | Hotel confirmation number — provide this, or use last_four_card together with check_in_date |
| string | optional | May be supplied in addition and must match, but is not a verification factor | |
| last_four_card | string | optional* | Last 4 digits of card used to book (requires check_in_date) |
| check_in_date | string | optional | Check-in date (YYYY-MM-DD) — required when using last_four_card |
first_name + last_name plus either confirmation_number, or last_four_card together with check_in_date. Email alone is not a verification factor.
{
"status": "confirmed",
"confirmation_number": "HLC-98234",
"hotel": "The Hermitage Hotel",
"check_in": "2026-06-12",
"check_out": "2026-06-15",
"guest_name": "Jane Smith"
}
resend_confirmation
Resend a confirmation using either the hotel confirmation number, or blind full-name + email recovery when the guest does not know it. Recovery never reveals whether a reservation matched and sends only to the email already on file. The email contains the hotel confirmation number needed for lookup or cancellation.
| Parameter | Type | Required | Description |
|---|---|---|---|
| confirmation_number | string | optional* | Hotel confirmation number; alternatively provide first_name, last_name, and email |
| first_name | string | optional* | Required with last_name and email for blind recovery |
| last_name | string | optional* | Required with first_name and email for blind recovery |
| string | optional* | Used to find a match; mail is sent only to the stored address |
{
"status": "sent",
"message": "Confirmation email has been resent to the email address on file."
}
cancel_booking
Create a secure cancellation handoff. The guest's name and hotel confirmation number must match. The tool returns the hotel's cancellation policy and a first-party 1Stay URL; it never cancels the reservation in the conversation.
| Parameter | Type | Required | Description |
|---|---|---|---|
| first_name | string | required | Guest's first name. Must match the booking record. |
| last_name | string | required | Guest's last name. Must match the booking record. |
| confirmation_number | string | required | Hotel confirmation number from the booking |
{
"status": "ACTION_REQUIRED",
"message": "Review and confirm on the secure cancellation page.",
"confirmation_number": "HLC-98234",
"cancellation_url": "https://book.1stay.ai/cancel/…",
"cancellation_url_expires_at": "2026-06-11T09:24:22Z",
"cancellation": { "policy_summary": "Hotel policy on file" }
}
search_tools
Discover available MCP tools and their capabilities at runtime. Useful for agents that want to dynamically understand what operations are supported without relying on static documentation.
No parameters required. Returns the complete list of tools with descriptions and parameter schemas. Accepts an optional keyword string (e.g. search, book, cancel, details) to filter the list.
{
"server": "1stay",
"version": "1.0.0",
"tools": [
{
"name": "search_hotels",
"description": "Search available hotels by location and dates",
"parameters": { /* schema */ }
}
// ... all 8 tools
]
}
Error
Codes
All errors return a JSON body with an error field (machine-readable code) and a message field (human-readable). Design your agent to handle these gracefully.
| HTTP | Error Code | When / What to Do |
|---|---|---|
| 200 | — | Idempotency hit — same key, booking already created. Returns original booking. |
| 201 | — | Booking created successfully. |
| 400 | invalid_request | Missing or invalid fields. Check guest data and required params. |
| 400 | cache_id_invalid | rate_code doesn't exist. Did you use a code from a different search? |
| 401 | unauthorized | API key missing, invalid, or revoked. |
| 409 | rate_changed | Rate changed between search and booking. See Rate Change Handling. |
| 410 | rate_unavailable | Room/rate no longer available. Search again. |
| 410 | cache_expired | Rate quote expired (~15 min). Call get_hotel_details again. |
| 429 | budget_exceeded | Daily search or booking budget exhausted. Resets midnight UTC. |
| 500 | booking_failed | Upstream error. No booking was created. Safe to retry with same idempotency key. |
Rate Change
Handling
Hotel rates are live inventory. Any price difference between when you called get_hotel_details and when you call book_hotel may trigger a 409 rate_changed response — even a difference of $0.01. No exceptions. No silent overrides. The guest always sees every change.
{
"error": "rate_changed",
"original_rate": { "nightly": 189.00, "total": 567.00 },
"new_rate": { "nightly": 209.00, "total": 627.00 },
"difference": { "nightly": 20.00, "total": 60.00, "direction": "increase" }
}
If you receive a rate_changed response, surface the new rate to the guest and let them decide. To proceed at the new rate, start a fresh book_hotel call. If the guest declines, call search_hotels again to find alternatives.
What your agent should say
| Scenario | Suggested Agent Response |
|---|---|
| Rate increased | "The hotel just updated this room from $189 to $209/night — that's $60 more for your stay. Want to book at the new rate or look at other options?" |
| Rate decreased | "Good news — the rate dropped from $209 to $189/night, saving you $60. Want me to lock it in?" |
| Small change | "The rate changed slightly from $189.00 to $189.47/night — likely a tax adjustment. That's $1.41 more total. Shall I proceed?" |
Access
Tiers
API capabilities and limits vary by access tier. All tiers use the same endpoints, response formats, and error codes — only the data volume and booking behavior differ.
| Capability | Sandbox | Production | Enterprise |
|---|---|---|---|
| Key prefix | sk_test_ |
sk_live_ |
sk_live_ |
| Hotels per search | 5 | 15 | Custom |
| Rate plans per hotel | 2 | All available | All available |
| Bookings | Simulated only | Live reservations | Live reservations |
| Searches / month | 100 / day | 50,000 included | Custom |
| Search overage | — | $0.002 / search | Custom |
| Monthly fee | Free (30 days) | $99 / mo | Custom |
| Platform fee | — | Small fee / booking | Custom |
| Booking service fee | — | Set your own via Stripe Connect | Custom structure |
| Cancellations | Simulated | Live | Live |
| Duration | 30 days | Ongoing | Contract term |
sk_test_ for sk_live_ and you're live. Response formats, error codes, and tool interfaces don't change.
Sandbox
Mode
Sandbox keys (sk_test_...) run against a test inventory rather than the production catalogue, and make no real reservations. The API surface is identical — same tools, same response shapes, same error codes — but it holds fewer properties and does not cover every destination. Most major cities populate; secondary markets and smaller towns may return nothing at all. An empty sandbox search is expected behaviour rather than an integration fault, and production coverage is not limited this way — if a search comes back empty, retry a major metro before debugging the call. Use sandbox for development and integration testing. Do not demo on it, and do not judge catalogue coverage from it.
book_hotel with a sk_test_ key returns a simulated confirmation. No hotel is contacted, no guest is charged, no reservation is created. This is enforced server-side and cannot be bypassed.
Switch from sandbox to production by swapping your key from sk_test_ to sk_live_. No other code changes required.
Questions? Email hello@1stay.ai.