# 1Stay Agent Integration Guide

> This document describes how AI agents should interact with 1Stay's hotel booking infrastructure through MCP tools.

## Connection

```json
{
  "1stay": {
    "url": "https://mcp.stayker.com/mcp"
  }
}
```

All tool calls require a Bearer token in the Authorization header.

- Sandbox keys: `sk_test_...` (simulated bookings only)
- Production keys: `sk_live_...` (live reservations)

---

## Booking Flow

Agents MUST follow this sequence. Steps cannot be skipped.

### Step 1: Search Hotels

```
search_hotels(
  location="Charlotte, NC",
  check_in="2026-05-06",
  check_out="2026-05-10",
  guests=2
)
```

Returns up to 15 hotels (production) or 5 hotels (sandbox) with live rates.

Each result includes a `hotel_id` — pass this to the next step.

### Step 2: Get Hotel Details

```
get_hotel_details(
  hotel_id="TP-MC-CLTQH",
  check_in="2026-05-06",
  check_out="2026-05-10",
  guests=2
)
```

Returns all available room types with:
- Nightly and total rates
- Cancellation policies
- `rate_code` (required for booking)

Rate holds are valid for **15 minutes**. After expiry, call `get_hotel_details` again.

### Step 3: Book Hotel

```
book_hotel(
  hotel_id="TP-MC-CLTQH",
  rate_code="rp_f8e2a1b3",
  check_in="2026-05-06",
  check_out="2026-05-10",
  guests=2,
  external_reference_id="unique-key-per-attempt"
)
```

Returns a secure checkout URL. Guest identity and payment details are collected
only on that page. After checkout, the guest email contains the hotel's own
confirmation number. Internal Stayker booking IDs are not exposed through MCP.

**The confirmation number is real.** The reservation exists in the hotel's system.

---

## Rate Change Handling

Hotel rates are live inventory. If the price changes between `get_hotel_details` and `book_hotel`, the API returns a **409 rate_changed** response.

```json
{
  "error": "rate_changed",
  "original_rate": 189.00,
  "new_rate": 199.00,
  "difference": 10.00
}
```

**Agent behavior:**
1. Present the price difference to the user
2. Ask if they want to proceed at the new rate
3. If yes, call `get_hotel_details` again for a fresh `rate_code`, then start a new checkout with a new `external_reference_id`
4. If no, search for alternatives

Never silently accept a rate change. The user must always be informed.

---

## Cancellation

```
cancel_booking(
  first_name="Jane",
  last_name="Smith",
  confirmation_number="HLC-98234"
)
```

Returns the hotel's cancellation policy and a secure first-party 1Stay URL.
The tool never cancels the reservation in conversation and never estimates a
refund, credit, penalty, or amount the hotel may charge. The guest must open
the URL, review the policy, and explicitly confirm on that page. Do not state
that the reservation is cancelled unless the secure page reports success.

---

## Booking Retrieval

```
get_booking(
  confirmation_number="HLC-98234",
  verification_token="token_from_lookup_booking"
)
```

Returns current booking status, confirmation details, hotel information, and cancellation policy.

---

## Idempotency

Use a stable `external_reference_id` for each distinct checkout attempt.

- Same reference = same checkout or confirmed reservation (prevents duplicates)
- A newly accepted rate requires a new reference because the old one is tied to the original checkout
- Generate a new UUID for each distinct booking attempt

---

## Error Handling

| Code | Error | Agent Should |
|------|-------|-------------|
| 400 | `invalid_params` | Check required fields and retry |
| 400 | `cache_id_invalid` | Rate expired — call `get_hotel_details` again |
| 404 | `hotel_not_found` | Hotel ID is wrong — search again |
| 409 | `rate_changed` | Present new rate to user, ask to proceed |
| 409 | `duplicate_booking` | Booking already exists with this idempotency key |
| 422 | `rate_expired` | Rate hold expired — call `get_hotel_details` again |
| 429 | `rate_limited` | Back off and retry after delay |
| 500 | `booking_failed` | Do NOT retry automatically — inform the user |

**Never retry a 500 booking error automatically.** A failed booking may have partially processed. Inform the user and suggest checking `get_booking` or contacting support.

---

## Rules for Agents

1. **Never skip steps.** search_hotels → get_hotel_details → book_hotel. Always in order.
2. **Never auto-accept rate changes.** Always inform the user.
3. **Never retry failed bookings silently.** A 500 on book_hotel could mean partial processing.
4. **Use external references for idempotency.** Reuse the same `external_reference_id` only for retries of the same checkout attempt.
5. **Never claim cancellation happened in conversation.** Send the guest to the secure 1Stay page and state only the hotel policy on file.
6. **Rate holds expire in 15 minutes.** If the user takes longer, call get_hotel_details again.
7. **Sandbox keys cannot create real bookings.** `sk_test_` keys return simulated confirmations only.
8. **Handle confirmation numbers by flow.** `book_hotel` does not return one; after checkout, tell the guest it is delivered by email. If a later verified `lookup_booking` call returns the confirmation number, present it clearly as the guest's proof of reservation.

---

## Tool Reference

| Tool | Purpose |
|------|---------|
| `search_hotels` | Find available hotels by location, dates, guests |
| `get_hotel_details` | Get property details, amenities, images, room types, rates, cancellation policies |
| `book_hotel` | Start secure checkout for exactly one room; accepts no guest identity or payment fields |
| `get_booking` | Retrieve booking status and details |
| `lookup_booking` | Look up a reservation with identity verification |
| `resend_confirmation` | Resend confirmation email to guest |
| `cancel_booking` | Create a secure first-party cancellation handoff; never cancels in conversation |
| `search_tools` | List all available tools and parameters |

---

## Links

- Documentation: https://1stay.ai/docs
- Apply for Access: https://1stay.ai/apply
- MCP Endpoint: https://mcp.stayker.com/mcp
- Contact: hello@1stay.ai
